やりたいこと
IBM API Connectは、OAuth2.0のトークン管理を外部のRDBMS等に依存することなく、DataPower Gatewayのみで実現する構成オプションを提供しています。RDBMSが不要なのでAvailabilityが上げやすかったり、構成がシンプルになったりと様々なメリットがあるのですが、残念ながらマニュアルの記載が不親切なため取っ付き難い機能でもあります。
今回はDataPower失効管理サービスに関連して良く話題にあがる「システムIDでのトークン失効」を、以下のように実現してみたいと思います。
- 通常のアプリとは別に、特権クライアントとして
Priviledged Client
を用意する - 特権クライアントからは、全
resource_owner
が全client
に対して付与したあらゆる許可を失効させることができる - 当然一般クライアントである
Hello App
からは特権機能は使えない
は本投稿で扱うAPI定義の概要です。この図で、右上の赤枠部分が今回のテーマの中心です。2つのProvider API
が同一のゲートウェイで稼働している場合、それぞれが発行したトークンは、もう一方のProvider API
からも取り消しが可能なようです。この動作を利用すると、外部公開されたOAuth External API
で発行したaccess_token
の権限を、非公開のOAuth Internal API
から取り消すことが可能になります。
図の左下部分(access_token
の発行)は一般的なOAuthフローなので今回は軽く流します。一般的なOAuthフローについて知りたい方はこちらの記事(英語)がお薦めです。
環境・前提
IBM API Connect v5.0.8.0, IBM DataPower Gateway v7.6.0.1 の組み合わせ(オンプレミス環境)で動作を確認しています。(DataPower失効管理サービスはIBM API ConnectのBluemix版では利用できません)
またIBM API ConnectのOAuth実装、および一般的なOAuth2.0に関する情報については説明を省きます。
実現方針
先程の図をエンドポイントレベルまで分解したものがこちらになります。2つのProvider API
は、それぞれリクエストを認証するためのAuthentication URL
と連係しています。access_token
発行時にユーザーを認証する/user
はLDAP等他の認証方式でも問題ありません。
同様に、コンポーネント間の処理の流れは以下のようになります。OAuth External API
は/user
が認証したユーザーを主体としたaccess_token
を発行しますが、OAuth Internal API
がこの権限を取り消すためには、Priviledged Client
によるOAuth External API
への取り消し要求時に、/system
が同じ主体に対して認証したとする応答を返す必要があります。
各コンポーネントの紹介
Open API
Hello API
- リソース・エンドポイント
- セキュリティ: OAuth2.0, Client ID (Header)
- 要求スコープは
scope1
-
Token URL
およびAuthorization URL
はOAuth External API
を参照
- 要求スコープは
今回のOAuthシナリオでリソース・エンドポイントとなる、Hello World!
と応答を返す基本的なAPIです。簡単のためバックエンド呼び出しも無く、あらゆるリクエストに対してGateway上で固定の応答を返します。ただしHello APIは Security Definition
として OAuth
による保護ポリシーが設定されており、呼び出しにはスコープ scope1
に対して適切な許可が与えられた access_token
を提示する必要があります。
Design
主な設定項目は以下の通りです。
info:
x-ibm-name: hello-api
title: Hello API
basePath: /message
securityDefinitions:
oauth:
type: oauth2
scopes:
scope1: ''
authorizationUrl: $(catalog-location)/ext/authorize
tokenUrl: $(catalog-location)/ext/token
clientIdHeader:
type: apiKey
in: header
name: X-IBM-Client-Id
security:
- oauth:
- scope1
clientIdHeader: []
paths:
/hello:
get:
Assembly
今回はset-variable
ポリシーで実装しています。
assembly:
execute:
- set-variable:
title: set-message
actions:
- set: message.body
value: '{"message":"Hello World!"}'
Client Application
Hello App
- OAuth2.0 機密クライアント
-
OAuth APIs
とBusiness APIs
両API製品のDefault Plan
にサブスクライブ -
Hello API
を呼び出すためのaccess_token
の発行を受ける -
Token Management APIs
にはサブスクライブしないため、トークン失効処理を行うことはできない - 本検証ではcurlコマンドで代用
外部のクライアント・アプリケーションに相当するコンポーネントです。OAuth2.0のフローに則ってaccess_token
の発行を受けます。
Privileged Client
- OAuth2.0 機密クライアント
-
Token Management APIs
API製品のDefault Plan
にサブスクライブ - 任意の
resource_owner
が任意のclient
に発行したaccess_token
やrefresh_token
を失効させることができる特権を持つ
管理者向けのセキュリティ基盤等、社内の周辺システムに相当するコンポーネントです。本シナリオではPrivileged Client
がOAuth Internal API
を使って、Hello App
向けに発行されたaccess_token
を失効(無効化)することが目標となります。
OAuth Provider API
OAuth External API
- 外部向けのOAuth Provider API
-
authorization_endpoint (/authorize)
、token_endpoint (/token)
を提供 -
introspection_endpoint (/introspect)
は説明の都合上提供しているが、必須では無い -
Enable Revocation
のトグルを有効化 - 認証URLとして
Authentication Service (/user)
を利用してresource_owner
を認証する - 認証URLからの応答に
API-Authenticated-Credential
ヘッダーが含まれた場合、このヘッダー値をaccess_token
およびrefresh_token
のresource_owner
とする - 本シナリオにおいてトークン発行機能を担う唯一のコンポーネント
Design
主な設定項目は以下の通りです。
info:
x-ibm-name: oauth-external-api
title: OAuth External API
basePath: /ext
paths:
/oauth2/authorize:
get:
summary: endpoint for Authorization Code and Implicit grants
post:
summary: submit approval to authorization code or access token
/oauth2/token:
post:
summary: Request Access Tokens
/oauth2/introspect:
post:
summary: Introspect a given access_token supported
x-ibm-configuration:
oauth2:
scopes:
scope1: Description 1
scope2: Description 2
scope3: Description 3
identity-extraction:
type: basic
authentication:
x-ibm-authentication-url:
url: $(catalog-location)/authenticate/user
authorization:
type: authenticated
access-token:
ttl: 3600
refresh-token:
count: 2048
ttl: 2682000
revocation:
type: gateway
type: oauth
OAuth Internal API
- 内部システム向けのOAuth Provider API
-
token_management_endpoint (/issued)
を提供 -
introspection_endpoint (/introspect)
は説明の都合上提供しているが、必須では無い - 認証URLとして
Authentication Service (/system)
を利用する - 認証URLからの応答に
API-Authenticated-Credential
ヘッダーが含まれた場合、このヘッダー値をresource_owner
として振る舞う- つまり
Priviledged System
は認証URLからのAPI-Authenticated-Credential
次第で任意のresource_owner
として権限を行使可能
- つまり
- 本シナリオにおいてトークン削除機能を担う唯一のコンポーネントであり、本検証の主役
info:
x-ibm-name: oauth-internal-api
title: OAuth Internal API
basePath: /int
paths:
/oauth2/authorize:
get:
summary: endpoint for Authorization Code and Implicit grants
post:
summary: submit approval to authorization code or access token
/oauth2/token:
post:
summary: Request Access Tokens
/oauth2/issued:
get:
summary: Returns list of permission granted to the owner
delete:
summary: Revoke an application/client permission by the authorized owner
/oauth2/introspect:
post:
summary: Introspect a given access_token supported
x-ibm-configuration:
oauth2:
scopes: {}
identity-extraction:
type: basic
authentication:
x-ibm-authentication-url:
url: $(catalog-location)/authenticate/system
authorization:
type: authenticated
access-token:
ttl: 3600
refresh-token:
count: 2048
ttl: 2682000
revocation:
type: gateway
type: oauth
Authorization URL
IBM API Connectで定義された認証URLを実装したエンドポイント。通常はGateway外に別途構築されますが、本稿のサンプルでは簡単のためGateway上に作ります。
Authentication Service (/user)
-
Authorization
ヘッダーの情報を元にユーザー認証を行い、認証成功時にはHTTPステータス・コード 200で応答する - 今回は固定のパスワード文字列
user-password
での認証とする - 認証されたユーザーIDを元に
cn=<user-id>,o=sample
の形式でサブジェクト文字列を生成し、応答のAPI-Authenticated-Credential
ヘッダーに含める
Authentication Service (/system)
- IBM API Connectで定義された認証URLを実装したエンドポイント
-
Authorization
ヘッダーの情報を元に認証を行い、認証成功時にはHTTPステータス・コード 200で応答する - 今回はどのようなパスワードでも無条件で認証OKとする
- 認証されたユーザーIDを元に
cn=<user-id>,o=sample
の形式でサブジェクト文字列を生成し、応答のAPI-Authenticated-Credential
ヘッダーに含める - 今回の検証の影の主役
Design
主な設定項目は以下の通りです。
info:
x-ibm-name: authentication-service
title: Authentication Service
basePath: /authenticate
securityDefinitions: {}
paths:
/user:
get:
parameters:
- name: Authorization
type: string
required: true
in: header
/system:
get:
parameters:
- name: Authorization
type: string
required: true
in: header
Assembly
今回はoperation-switch
とgatewayscript
ポリシーを組み合わせて実装しています。
var authHeaderStr = apim.getvariable('request.headers.authorization');
if (authHeaderStr) {
var authHeaders = authHeaderStr.split(' ');
if (authHeaders.length >= 2 && authHeaders[0] == 'Basic') {
// authHeaderが存在し、Basicスキームの場合のみ、base64デコードを実施
var authCreds = new Buffer(authHeaders[1], 'base64').toString('ascii').split(':');
var credential = {
name:authCreds[0],
secret:authCreds[1],
validated:false
}
}
}
if (credential) {
apim.setvariable('authorization-credential', credential);
} else {
apim.error('ValidationFailure', 400, 'Bad Request', 'Unable to parse Authorization Header.');
}
var credential = apim.getvariable('authorization-credential');
if (credential && credential.secret=='user-password') {
credential.validated = true;
apim.setvariable('authorization-credential', credential);
}
var credential = apim.getvariable('authorization-credential');
if (true) {
credential.validated = true;
apim.setvariable('authorization-credential', credential);
}
var credential= apim.getvariable('authorization-credential');
if (credential.validated) {
credential.subject = 'cn='+credential.name+',o=sample';
credential.secret='*******';
// 応答の書き出し処理
apim.setvariable('message.headers.API-Authenticated-Credential', credential.subject);
session.output.write(credential);
apim.output('application/json');
} else {
apim.error('ValidationFailure', 403, 'Forbidden', 'User credential validateion failed.');
}
動作確認
サブスクリプションの確認
事前に二つのクライアントを登録し、client_id
,client_secret
の発行とサブスクリプションを完了しておきます。
access_token
の発行
面倒なのでPasswordフローを使います
C:\Users\me>set catalog_location=https://<domain>/<provider-org>/<catalog>
C:\Users\me>set client_id=54d0ae98-78c9-4300-9a24-c1acf606de67
C:\Users\me>set client_secret=***********************************
C:\Users\me>set user_id=user01
C:\Users\me>set user_secret=user-password
C:\Users\me>set oauth_scope=scope1
C:\Users\me> curl -k -i -d "grant_type=password&client_id=%client_id%&client_secret=%client_secret%&scope=%oauth_scope%&username=%user_id%&password=%user_secret%" -H "Content-Type: application/x-www-form-urlencoded" -X POST "%catalog_location%/ext/oauth2/token"
HTTP/1.1 200 OK
X-Backside-Transport: FAIL FAIL
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/json
Cache-Control: private, no-store, no-cache, must-revalidate
Pragma: no-cache
Content-Security-Policy: default-src 'self'; style-src 'unsafe-inline'
Access-Control-Expose-Headers: APIm-Debug-Trans-Id, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-Global-Transaction-ID
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST
Access-Control-Allow-Credentials: false
{ "token_type":"bearer", "access_token":"AAIkNTRkMGFlOTgtNzhjOS00MzAwLTlhMjQtYzFhY2Y2MDZkZTY3gDh9eHOEulPLXISXeKmKIloQylIakqtodiZebcBegLumWkZIp4InjHxDgoY0RXokPAyi5B0Nk0YSAT1Zlls3YfWm3HEd9BadFJbrPTLoQ7RsuyBF2fpqP08KTg3UYPnv", "expires_in":3600, "consented_on":1506477368, "scope":"scope1", refresh_token":"AAJUpJXZBfoOpp3-Z9i0LD_yn_XZgnHxMtlna4bnt21B8ke-DDcdKkVKtMy2xCEdRnplTR2GLobsJ_hdIbsl7McLXqzFFTyLRnbjavecSTGUlj8ReE5rlZtKWiqaEGJoFCOaNSUJMw0m89wUr3swPmps", "refresh_token_expires_in":2682000 }
access_token
の確認
Introspectionを使ってトークンの内容を確認すると、subject cn=user01,o=sample
に対して発行されていることが分かります。
C:\Users\me>set catalog_location=https://<domain>/<provider-org>/<catalog>
C:\Users\me>set client_id=54d0ae98-78c9-4300-9a24-c1acf606de67
C:\Users\me>set client_secret=***********************************
C:\Users\me>set oauth_access_token=AAIkNTRkMGFlOTgtNzhjOS00MzAwLTlhMjQtYzFhY2Y2MDZkZTY3gDh9eHOEulPLXISXeKmKIloQylIakqtodiZebcBegLumWkZIp4InjHxDgoY0RXokPAyi5B0Nk0YSAT1Zlls3YfWm3HEd9BadFJbrPTLoQ7RsuyBF2fpqP08KTg3UYPnv
C:\Users\me>set token_type_hint=access_token
C:\Users\me>curl -k -i -d "token=%oauth_access_token%token_type_hint=%token_type_hint%" -H "Content-Type: application/x-www-form-urlencoded" -H "X-IBM-Client-Id: %client_id%" -H "X-IBM-Client-Secret: %client_secret%" -X POST "%catalog_location%/ext/oauth2/introspect"
HTTP/1.1 200 OK
X-Backside-Transport: FAIL FAIL
Connection: Keep-Alive
Transfer-Encoding: chunked
Cache-Control: private, no-store, no-cache, must-revalidate
Pragma: no-cache
Content-Security-Policy: default-src 'self'; style-src 'unsafe-inline'
Access-Control-Expose-Headers: APIm-Debug-Trans-Id, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-Global-Transaction-ID
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST
Access-Control-Allow-Credentials: false
Content-Type: application/json; charset=UTF-8
{ "active":true, "token_type":"bearer", "client_id":"54d0ae98-78c9-4300-9a24-c1acf606de67", "username":"cn=user01,o=sample", "sub":"cn=user01,o=sample", "exp":1506480968, "expstr":"2017-09-27T02:56:08Z", "iat":1506477368, "nbf":1506477368,"nbfstr":"2017-09-27T01:56:08Z", "scope":"scope1", "miscinfo":"[r:gateway]", "consented_on":1506477368, "consented_on_str":"2017-09-27T01:56:08Z", "grant_type":"password", "client_name":"Hello App" }
Hello API の呼び出し
access_token
を使ってHello APIの呼び出しが成功することを確認します。
C:\Users\me>set catalog_location=https://<domain>/<provider-org>/<catalog>
C:\Users\me>set client_id=54d0ae98-78c9-4300-9a24-c1acf606de67
C:\Users\me>set oauth_access_token=AAIkNTRkMGFlOTgtNzhjOS00MzAwLTlhMjQtYzFhY2Y2MDZkZTY3gDh9eHOEulPLXISXeKmKIloQylIakqtodiZebcBegLumWkZIp4InjHxDgoY0RXokPAyi5B0Nk0YSAT1Zlls3YfWm3HEd9BadFJbrPTLoQ7RsuyBF2fpqP08KTg3UYPnv
C:\Users\me>curl -k -i -H "Authorization: Bearer %oauth_access_token%" -H "X-IBM-Client-Id: %client_id%" -X GET "%catalog_location%/message/hello"
HTTP/1.1 200 OK
X-Backside-Transport: OK OK
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: unknown
Date: Wed, 27 Sep 2017 02:16:11 GMT
X-Global-Transaction-ID: 196c556559cb09eb0018afc1
User-Agent: IBM-APIConnect/5.0
Accept: */*
Via: 1.1 AQAAAIAXUgo-
X-Client-IP: 192.168.75.1
IBM-App-User: cn=user01,o=sample
Access-Control-Expose-Headers: APIm-Debug-Trans-Id, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-Globa
l-Transaction-ID
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET
Access-Control-Allow-Credentials: false
{"message":"Hello World!"}
access_token
の取り消し
Internal OAuth ProviderをPriviledged Clientのclient_id
,client_secret
で呼び出すことで、Hello Appに発行されたaccess_token
を取り消します。この際、Hello Appやresource_owner (user01)のパスワードは必要ありません(ブランクでOK)。
C:\Users\me>set catalog_location=https://<domain>/<provider-org>/<catalog>
C:\Users\me>set client_id=2779c8e6-105f-40c2-b41a-4ad44d6ef34e
C:\Users\me>set client_secret=******************************
C:\Users\me>set target_user_id=user01
C:\Users\me>set target_client_id=54d0ae98-78c9-4300-9a24-c1acf606de67
C:\Users\me>curl -k -i -u "%target_user_id%:" -H "X-IBM-Client-Id: %client_id%" -H "X-IBM-Client-Secret: %client_secret%" -X DELETE "%catalog_location%/int/oauth2/issued?client-id=%target_client_id%"
HTTP/1.1 200 OK
X-Backside-Transport: FAIL FAIL
Connection: Keep-Alive
Transfer-Encoding: chunked
Cache-Control: private, no-store, no-cache, must-revalidate
Pragma: no-cache
Content-Security-Policy: default-src 'self'; style-src 'unsafe-inline'
Access-Control-Expose-Headers: APIm-Debug-Trans-Id, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-Global-Transaction-ID
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: DELETE
Access-Control-Allow-Credentials: false
Content-Type: application/json; charset=UTF-8
{ "status":"success" }
access_token
の取り消し確認
再度access_token
でHello APIを呼び出すと、失敗します。
C:\Users\me>set catalog_location=https://<domain>/<provider-org>/<catalog>
C:\Users\me>set client_id=54d0ae98-78c9-4300-9a24-c1acf606de67
C:\Users\me>set oauth_access_token=AAIkNTRkMGFlOTgtNzhjOS00MzAwLTlhMjQtYzFhY2Y2MDZkZTY3gDh9eHOEulPLXISXeKmKIloQylIakqtodiZebcBegLumWkZIp4InjHxDgoY0RXokPAyi5B0Nk0YSAT1Zlls3YfWm3HEd9BadFJbrPTLoQ7RsuyBF2fpqP08KTg3UYPnv
C:\Users\me>curl -k -i -H "Authorization: Bearer %oauth_access_token%" -H "X-IBM-Client-Id: %client_id%" -X GET "%catalog_location%/message/hello"
HTTP/1.1 401 Unauthorized
Content-Type: application/json
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: false
Access-Control-Expose-Headers: APIm-Debug-Trans-Id, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-Global-Transaction-ID
WWW-Authenticate: Bearer error="invalid_token",error_description="permission is revoked or it cannot be reused"
X-Backside-Transport: FAIL FAIL
Connection: close
{ "httpCode":"401", "httpMessage":"Unauthorized", "moreInformation":"This server could not verify that you are authorized to access the URL" }
参考情報
IBM developerWorks Blog - Securing APIs using OAuth in API Connect – Video Tutorial
IBM developerWorks - IBM API Connect OAuth構成概要と構成例
IBM Knowledge Center - Authentication URL
IBM Knowledge Center - GatewayScript
IBM Knowledge Center - API Connect context variables
コード
今回作成したコードがこちらです。catalog-location
を適当な値に置き換えてご利用ください。
x-ibm-configuration:
properties:
catalog-location:
value: "https://<domain>/<provider-org>/<catalog>"
API YAML
hello-api
---
swagger: "2.0"
info:
x-ibm-name: "hello-api"
title: "Hello API"
version: "1.0.0"
schemes:
- "https"
host: "$(catalog.host)"
basePath: "/message"
consumes:
- "application/json"
produces:
- "application/json"
securityDefinitions:
oauth:
type: "oauth2"
description: ""
flow: "accessCode"
scopes:
scope1: ""
authorizationUrl: "$(catalog-location)/ext/authorize"
tokenUrl: "$(catalog-location)/ext/token"
x-tokenIntrospect:
url: ""
clientIdHeader:
type: "apiKey"
in: "header"
name: "X-IBM-Client-Id"
security:
- oauth:
- "scope1"
clientIdHeader: []
x-ibm-configuration:
testable: true
enforced: true
cors:
enabled: true
assembly:
execute:
- set-variable:
title: "set-message"
actions:
- set: "message.body"
value: "{\"message\":\"Hello World!\"}"
version: "1.0.0"
phase: "realized"
properties:
catalog-location:
value: "https://<domain>/<provider-org>/<catalog>"
description: ""
encoded: false
paths:
/hello:
get:
responses:
200:
description: "200 OK"
definitions: {}
tags: []
authentication-service
---
swagger: "2.0"
info:
x-ibm-name: "authentication-service"
title: "Authentication Service"
version: "1.0.0"
schemes:
- "https"
host: "$(catalog.host)"
basePath: "/authenticate"
consumes:
- "application/json"
produces:
- "application/json"
securityDefinitions: {}
x-ibm-configuration:
testable: true
enforced: true
cors:
enabled: true
assembly:
execute:
- gatewayscript:
title: "extract-identity"
version: "1.0.0"
source: "var authHeaderStr = apim.getvariable('request.headers.authorization');\r\
\nif (authHeaderStr) {\r\n\tvar authHeaders = authHeaderStr.split(' ');\r\
\n\tif (authHeaders.length >= 2 && authHeaders[0] == 'Basic') { \r\n \
\ // authHeaderが存在し、Basicスキームの場合のみ、base64デコードを実施\r\n var authCreds\
\ = new Buffer(authHeaders[1], 'base64').toString('ascii').split(':');\r\
\n var credential = {\r\n name:authCreds[0],\r\n \
\ secret:authCreds[1],\r\n validated:false\r\n }\r\
\n\t}\r\n}\r\nif (credential) {\r\n apim.setvariable('authorization-credential',\
\ credential);\r\n} else {\r\n apim.error('ValidationFailure', 400, 'Bad\
\ Request', 'Unable to parse Authorization Header.');\r\n}\r\n"
- operation-switch:
title: "operation-switch"
case:
- operations:
- verb: "get"
path: "/user"
execute:
- gatewayscript:
title: "validate-user"
version: "1.0.0"
source: "var credential = apim.getvariable('authorization-credential');\r\
\n\r\nif (credential && credential.secret=='user-password') {\r\n\
\ credential.validated = true;\r\n apim.setvariable('authorization-credential',\
\ credential);\r\n}"
- operations:
- verb: "get"
path: "/system"
execute:
- gatewayscript:
title: "validate-system"
version: "1.0.0"
source: "var credential = apim.getvariable('authorization-credential');\r\
\n\r\nif (true) {\r\n credential.validated = true;\r\n apim.setvariable('authorization-credential',\
\ credential);\r\n}"
otherwise: []
version: "1.0.0"
- gatewayscript:
title: "set-response"
version: "1.0.0"
source: "var credential= apim.getvariable('authorization-credential');\r\n\
\r\nif (credential.validated) {\r\n credential.subject = 'cn='+credential.name+',o=sample';\r\
\n credential.secret='*******';\r\n\r\n // 応答の書き出し処理\r\n apim.setvariable('message.headers.API-Authenticated-Credential',\
\ credential.subject);\r\n session.output.write(credential);\r\n apim.output('application/json');\r\
\n} else {\r\n apim.error('ValidationFailure', 403, 'Forbidden', 'User\
\ credential validateion failed.');\r\n}"
- activity-log:
title: "activity-log"
content: "payload"
error-content: "payload"
version: "1.0.0"
catch: []
phase: "realized"
paths:
/user:
get:
responses:
200:
description: "200 OK"
parameters:
- name: "Authorization"
type: "string"
required: true
in: "header"
/system:
get:
responses:
200:
description: "200 OK"
parameters:
- name: "Authorization"
type: "string"
required: true
in: "header"
definitions: {}
tags: []
security: []
oauth-external-api
---
swagger: "2.0"
info:
x-ibm-name: "oauth-external-api"
title: "OAuth External API"
version: "1.0.0"
schemes:
- "https"
host: "$(catalog.host)"
basePath: "/ext"
securityDefinitions:
clientID:
description: "application's client_id"
in: "query"
name: "client_id"
type: "apiKey"
clientIdHeader:
in: "header"
type: "apiKey"
name: "X-IBM-Client-Id"
clientSecretHeader:
in: "header"
type: "apiKey"
name: "X-IBM-Client-Secret"
security:
- {}
paths:
/oauth2/authorize:
get:
produces:
- "text/html"
summary: "endpoint for Authorization Code and Implicit grants"
description: "description"
parameters:
- name: "response_type"
in: "query"
description: "request an authorization code or or access token (implicit)"
required: true
type: "string"
enum:
- "code"
- "token"
- name: "scope"
in: "query"
description: "Scope being requested"
type: "string"
required: true
- name: "redirect_uri"
in: "query"
type: "string"
description: "URI where user is redirected to after authorization"
required: false
- name: "state"
in: "query"
type: "string"
description: "This string will be echoed back to application when user is\
\ redirected"
required: false
responses:
200:
description: "An HTML form for authentication or authorization of this request."
302:
description: "Redirect to the clients redirect_uri containing one of the\
\ following\n- **authorization code** for Authorization code grant\n-\
\ **access token** for Implicity grant\n- **error** in case of errors,\
\ such as the user has denied the request\n"
security:
- clientID: []
post:
consumes:
- "application/x-www-form-urlencoded"
produces:
- "text/html"
summary: "submit approval to authorization code or access token"
description: "Submit resource owners approval (or rejection) for the OAuth2\
\ Server to issue an\nauthorization code or access token to the application.\n"
security: []
parameters:
- name: "client_id"
in: "formData"
description: "application requesting the access code or token"
required: true
type: "string"
- name: "scope"
in: "formData"
description: "requested scope of this authorization"
required: true
type: "string"
- name: "resource-owner"
in: "formData"
description: "resource owners user name"
required: true
type: "string"
- name: "redirect_uri"
in: "formData"
description: "URI the application is requesting this code or token to be redirected\
\ to"
required: true
type: "string"
- name: "original-url"
in: "formData"
description: "URL of the original authorization request"
required: true
type: "string"
- name: "dp-state"
in: "formData"
description: "state information provided in the authorization form"
required: true
type: "string"
- name: "dp-data"
in: "formData"
description: "state information provided in the authorization form"
required: true
type: "string"
responses:
200:
description: "A consent form for oauth processing."
/oauth2/token:
post:
consumes:
- "application/x-www-form-urlencoded"
produces:
- "application/json"
summary: "Request Access Tokens"
description: "This endpoint allows requesting an access token following one\
\ of the flows below:\n- Authorization Code (exchange code for access token)\n\
- Client Credentials (2-legged, there isnt resource owner information)\n-\
\ Resource Owner Password Credentials (2-legged, client provides resource\
\ owner name and password)\n- Refresh Token (exchange refresh token for a\
\ new access code)\n\nThe table below indicates the required parameters for\
\ each specific grant_type options.\nEmpty cells indicate a parameter is ignored\
\ for that specific grant type.\n\nClient authentication:\n- Confidential\
\ clients should authenticate using HTTP Basic Authentication. Alternatively,\
\ they may post\n their client_id and client_secret information as a formData\
\ parameter.\n- Public clients should send their client_id as formData parameter.\n\
\n| grant_type | code | client_credentials | password |\
\ refresh_token |\n|----------------------|------------|--------------------|-------------|---------------|\n\
| client_id | required* | required* | required* | required*\
\ |\n| client_secret | required* | required* | required*\
\ | required* |\n| code | required | \
\ | | |\n| redirect_uri | required\
\ | | | |\n| username \
\ | | | required | |\n\
| password | | | required | \
\ |\n| scope | | optional \
\ | optional | |\n| refresh_token | |\
\ | | required |\n\nThe implicit grant\
\ requests, see /oauth2/authorize.\n"
security: []
parameters:
- name: "grant_type"
in: "formData"
description: "Type of grant"
type: "string"
required: true
enum:
- "authorization_code"
- "password"
- "client_credentials"
- "refresh_token"
- name: "client_id"
in: "formData"
description: "Application client ID, can be provided in formData or using\
\ HTTP Basic Authentication"
required: false
type: "string"
- name: "client_secret"
in: "formData"
description: "Application secret, must be provided in formData or using HTTP\
\ Basic Authentication"
required: false
type: "string"
- name: "code"
in: "formData"
description: "Authorization code provided by the /oauth2/authorize endpoint"
required: false
type: "string"
- name: "redirect_uri"
in: "formData"
description: "required only if the redirect_uri parameter was included in\
\ the authorization request /oauth2/authorize; their values MUST be identical."
required: false
type: "string"
- name: "username"
in: "formData"
type: "string"
description: "Resource owner username"
required: false
- name: "password"
in: "formData"
type: "string"
description: "Resource owner password"
required: false
- name: "scope"
in: "formData"
type: "string"
description: "Scope being requested"
required: false
- name: "refresh_token"
in: "formData"
type: "string"
description: "The refresh token that the client wants to exchange for a new\
\ access token (refresh_token grant_type)"
required: false
responses:
200:
description: "json document containing token, etc."
schema:
$ref: "#/definitions/access_token_response"
400:
description: "json document that may contain additional details about the\
\ failure"
/oauth2/introspect:
post:
consumes:
- "application/x-www-form-urlencoded"
produces:
- "application/json"
summary: "Introspect a given access_token supported"
description: "This endpoint introspects a given access_token\n"
security:
- clientIdHeader: []
clientSecretHeader: []
parameters:
- name: "token"
in: "formData"
description: "String value of the access_token to be introspected"
required: true
type: "string"
- name: "token_type_hint"
in: "formData"
description: "This must contain 'access_token' to indicate the token type"
required: true
type: "string"
responses:
200:
description: "json document containing access_token information, etc."
schema:
$ref: "#/definitions/introspect_response"
401:
description: "failure"
x-ibm-configuration:
testable: true
enforced: true
phase: "realized"
oauth2:
client-type: "confidential"
scopes:
scope1: "Description 1"
scope2: "Description 2"
scope3: "Description 3"
grants:
- "application"
- "password"
- "accessCode"
- "implicit"
identity-extraction:
type: "basic"
authentication:
x-ibm-authentication-url:
url: "$(catalog-location)/authenticate/user"
tls-profile: ""
authorization:
type: "authenticated"
access-token:
ttl: 3600
refresh-token:
count: 2048
ttl: 2682000
metadata:
metadata-url:
url: ""
tls-profile: ""
revocation:
type: "gateway"
cors:
enabled: true
type: "oauth"
properties:
catalog-location:
value: "https://<domain>/<provider-org>/<catalog>"
description: ""
encoded: false
definitions:
access_token_response:
type: "object"
additionalProperties: false
required:
- "token_type"
- "access_token"
- "expires_in"
properties:
token_type:
enum:
- "bearer"
access_token:
type: "string"
expires_in:
type: "integer"
scope:
type: "string"
refresh_token:
type: "string"
introspect_response:
type: "object"
additionalProperties: false
required:
- "active"
- "client_id"
- "client_name"
- "username"
- "sub"
- "exp"
- "expstr"
- "iat"
- "nbf"
- "nbfstr"
- "scope"
properties:
active:
type: "boolean"
client_id:
type: "string"
client_name:
type: "string"
username:
type: "string"
sub:
type: "string"
exp:
type: "string"
expstr:
type: "string"
iat:
type: "string"
nbf:
type: "string"
nbfstr:
type: "string"
scope:
type: "string"
miscinfo:
type: "string"
consented_on:
type: "string"
consented_on_str:
type: "string"
grant_type:
type: "string"
oauth-internal-api
---
swagger: "2.0"
info:
x-ibm-name: "oauth-internal-api"
title: "OAuth Internal API"
version: "1.0.0"
schemes:
- "https"
host: "$(catalog.host)"
basePath: "/int"
securityDefinitions:
clientID:
description: "application's client_id"
in: "query"
name: "client_id"
type: "apiKey"
clientIdHeader:
in: "header"
type: "apiKey"
name: "X-IBM-Client-Id"
clientSecretHeader:
in: "header"
type: "apiKey"
name: "X-IBM-Client-Secret"
security:
- clientID: []
paths:
/oauth2/authorize:
get:
produces:
- "text/html"
summary: "endpoint for Authorization Code and Implicit grants"
description: "description"
parameters:
- name: "response_type"
in: "query"
description: "request an authorization code or or access token (implicit)"
required: true
type: "string"
enum:
- "code"
- "token"
- name: "scope"
in: "query"
description: "Scope being requested"
type: "string"
required: true
- name: "redirect_uri"
in: "query"
type: "string"
description: "URI where user is redirected to after authorization"
required: false
- name: "state"
in: "query"
type: "string"
description: "This string will be echoed back to application when user is\
\ redirected"
required: false
responses:
200:
description: "An HTML form for authentication or authorization of this request."
302:
description: "Redirect to the clients redirect_uri containing one of the\
\ following\n- **authorization code** for Authorization code grant\n-\
\ **access token** for Implicity grant\n- **error** in case of errors,\
\ such as the user has denied the request\n"
security:
- clientID: []
post:
consumes:
- "application/x-www-form-urlencoded"
produces:
- "text/html"
summary: "submit approval to authorization code or access token"
description: "Submit resource owners approval (or rejection) for the OAuth2\
\ Server to issue an\nauthorization code or access token to the application.\n"
security: []
parameters:
- name: "client_id"
in: "formData"
description: "application requesting the access code or token"
required: true
type: "string"
- name: "scope"
in: "formData"
description: "requested scope of this authorization"
required: true
type: "string"
- name: "resource-owner"
in: "formData"
description: "resource owners user name"
required: true
type: "string"
- name: "redirect_uri"
in: "formData"
description: "URI the application is requesting this code or token to be redirected\
\ to"
required: true
type: "string"
- name: "original-url"
in: "formData"
description: "URL of the original authorization request"
required: true
type: "string"
- name: "dp-state"
in: "formData"
description: "state information provided in the authorization form"
required: true
type: "string"
- name: "dp-data"
in: "formData"
description: "state information provided in the authorization form"
required: true
type: "string"
responses:
200:
description: "A consent form for oauth processing."
/oauth2/token:
post:
consumes:
- "application/x-www-form-urlencoded"
produces:
- "application/json"
summary: "Request Access Tokens"
description: "This endpoint allows requesting an access token following one\
\ of the flows below:\n- Authorization Code (exchange code for access token)\n\
- Client Credentials (2-legged, there isnt resource owner information)\n-\
\ Resource Owner Password Credentials (2-legged, client provides resource\
\ owner name and password)\n- Refresh Token (exchange refresh token for a\
\ new access code)\n\nThe table below indicates the required parameters for\
\ each specific grant_type options.\nEmpty cells indicate a parameter is ignored\
\ for that specific grant type.\n\nClient authentication:\n- Confidential\
\ clients should authenticate using HTTP Basic Authentication. Alternatively,\
\ they may post\n their client_id and client_secret information as a formData\
\ parameter.\n- Public clients should send their client_id as formData parameter.\n\
\n| grant_type | code | client_credentials | password |\
\ refresh_token |\n|----------------------|------------|--------------------|-------------|---------------|\n\
| client_id | required* | required* | required* | required*\
\ |\n| client_secret | required* | required* | required*\
\ | required* |\n| code | required | \
\ | | |\n| redirect_uri | required\
\ | | | |\n| username \
\ | | | required | |\n\
| password | | | required | \
\ |\n| scope | | optional \
\ | optional | |\n| refresh_token | |\
\ | | required |\n\nThe implicit grant\
\ requests, see /oauth2/authorize.\n"
security: []
parameters:
- name: "grant_type"
in: "formData"
description: "Type of grant"
type: "string"
required: true
enum:
- "authorization_code"
- "password"
- "client_credentials"
- "refresh_token"
- name: "client_id"
in: "formData"
description: "Application client ID, can be provided in formData or using\
\ HTTP Basic Authentication"
required: false
type: "string"
- name: "client_secret"
in: "formData"
description: "Application secret, must be provided in formData or using HTTP\
\ Basic Authentication"
required: false
type: "string"
- name: "code"
in: "formData"
description: "Authorization code provided by the /oauth2/authorize endpoint"
required: false
type: "string"
- name: "redirect_uri"
in: "formData"
description: "required only if the redirect_uri parameter was included in\
\ the authorization request /oauth2/authorize; their values MUST be identical."
required: false
type: "string"
- name: "username"
in: "formData"
type: "string"
description: "Resource owner username"
required: false
- name: "password"
in: "formData"
type: "string"
description: "Resource owner password"
required: false
- name: "scope"
in: "formData"
type: "string"
description: "Scope being requested"
required: false
- name: "refresh_token"
in: "formData"
type: "string"
description: "The refresh token that the client wants to exchange for a new\
\ access token (refresh_token grant_type)"
required: false
responses:
200:
description: "json document containing token, etc."
schema:
$ref: "#/definitions/access_token_response"
400:
description: "json document that may contain additional details about the\
\ failure"
/oauth2/issued:
get:
produces:
- "application/json"
summary: "Returns list of permission granted to the owner"
description: "This endpoint allows the return of all the issued permission for\
\ a given authenticated owner per owner authentication \ndefined in the x-ibm-configuration\
\ section\n"
security:
- clientIdHeader: []
clientSecretHeader: []
responses:
200:
description: "json document containing issued information, etc."
schema:
$ref: "#/definitions/issued_responses"
401:
description: "failure in retreiving issued list"
delete:
consumes:
- "application/x-www-form-urlencoded"
produces:
- "application/json"
summary: "Revoke an application/client permission by the authorized owner"
description: "Revoke an application/client permission by the authorized owner"
security:
- clientIdHeader: []
clientSecretHeader: []
parameters:
- name: "client-id"
in: "query"
description: "client-id is the OAuth client_id or application id to be revoked"
required: true
type: "string"
responses:
200:
description: "OK"
/oauth2/introspect:
post:
consumes:
- "application/x-www-form-urlencoded"
produces:
- "application/json"
summary: "Introspect a given access_token supported"
description: "This endpoint introspects a given access_token\n"
security:
- clientIdHeader: []
clientSecretHeader: []
parameters:
- name: "token"
in: "formData"
description: "String value of the access_token to be introspected"
required: true
type: "string"
- name: "token_type_hint"
in: "formData"
description: "This must contain 'access_token' to indicate the token type"
required: true
type: "string"
responses:
200:
description: "json document containing access_token information, etc."
schema:
$ref: "#/definitions/introspect_response"
401:
description: "failure"
x-ibm-configuration:
testable: true
enforced: true
phase: "realized"
oauth2:
client-type: "confidential"
scopes: {}
grants:
- "implicit"
- "application"
- "password"
- "accessCode"
identity-extraction:
type: "basic"
authentication:
x-ibm-authentication-url:
url: "$(catalog-location)/authenticate/system"
tls-profile: ""
authorization:
type: "authenticated"
access-token:
ttl: 3600
refresh-token:
count: 2048
ttl: 2682000
revocation:
type: "gateway"
metadata:
metadata-url:
url: ""
tls-profile: ""
cors:
enabled: true
type: "oauth"
properties:
catalog-location:
value: "https://<domain>/<provider-org>/<catalog>"
description: ""
encoded: false
definitions:
access_token_response:
type: "object"
additionalProperties: false
required:
- "token_type"
- "access_token"
- "expires_in"
properties:
token_type:
enum:
- "bearer"
access_token:
type: "string"
expires_in:
type: "integer"
scope:
type: "string"
refresh_token:
type: "string"
issued_response:
type: "object"
additionalProperties: false
required:
- "clientId"
- "owner"
- "scope"
- "issuedAt"
- "expiredAt"
- "refreshTokenIssued"
properties:
clientId:
type: "string"
clientName:
type: "string"
owner:
type: "string"
scope:
type: "string"
issuedAt:
type: "string"
expiredAt:
type: "string"
refreshTokenIssued:
type: "boolean"
miscInfo:
type: "string"
consentedOn:
type: "string"
appId:
type: "string"
org:
type: "string"
orgId:
type: "string"
provider:
type: "string"
providerId:
type: "string"
catalog:
type: "string"
catalogId:
type: "string"
issued_responses:
type: "array"
items:
$ref: "#/definitions/issued_response"
introspect_response:
type: "object"
additionalProperties: false
required:
- "active"
- "client_id"
- "client_name"
- "username"
- "sub"
- "exp"
- "expstr"
- "iat"
- "nbf"
- "nbfstr"
- "scope"
properties:
active:
type: "boolean"
client_id:
type: "string"
client_name:
type: "string"
username:
type: "string"
sub:
type: "string"
exp:
type: "string"
expstr:
type: "string"
iat:
type: "string"
nbf:
type: "string"
nbfstr:
type: "string"
scope:
type: "string"
miscinfo:
type: "string"
consented_on:
type: "string"
consented_on_str:
type: "string"
grant_type:
type: "string"
Product YAML
business-apis
---
product: "1.0.0"
info:
name: "business-apis"
title: "Business APIs"
version: "1.0.0"
visibility:
view:
enabled: true
type: "public"
tags: []
orgs: []
subscribe:
enabled: true
type: "authenticated"
tags: []
orgs: []
apis:
hello-api-gateway:
$ref: "hello-api_1.0.0.yaml"
plans:
Default:
title: "Default Plan"
backend-apis
---
product: "1.0.0"
info:
name: "backend-apis"
title: "Backend APIs"
version: "1.0.0"
visibility:
view:
enabled: true
type: "public"
tags: []
orgs: []
subscribe:
enabled: true
type: "authenticated"
tags: []
orgs: []
apis:
authentication-service:
$ref: "authentication-service_1.0.0.yaml"
plans:
default:
title: "Default Plan"
description: "Default Plan"
approval: false
rate-limit:
hard-limit: false
value: "100/hour"
oauth-apis
---
product: "1.0.0"
info:
name: "oauth-apis"
title: "OAuth APIs"
version: "1.0.0"
visibility:
view:
enabled: true
type: "public"
tags: []
orgs: []
subscribe:
enabled: true
type: "authenticated"
tags: []
orgs: []
apis:
oauth-apis:
$ref: "oauth-external-api_1.0.0.yaml"
plans:
default:
title: "Default Plan"
description: "Default Plan"
approval: false
rate-limit:
hard-limit: false
value: "100/hour"
token-management-apis
---
product: "1.0.0"
info:
name: "token-management-apis"
title: "Token Management APIs"
version: "1.0.0"
visibility:
view:
enabled: true
type: "public"
tags: []
orgs: []
subscribe:
enabled: true
type: "authenticated"
tags: []
orgs: []
apis:
oauth-internal-api:
$ref: "oauth-internal-api_1.0.0.yaml"
plans:
default:
title: "Default Plan"
description: "Default Plan"
approval: false
rate-limit:
hard-limit: false
value: "100/hour"