このトピックでは、OpenID Connect(OIDC)プロトコルを使用して、カスタム アプリケーションを IDaaS のシングルサインオン(SSO)サービスと統合する方法について説明します。 これにより、開発者はアプリケーションの SSO 機能を簡単に実装し、ユーザーエクスペリエンスと管理効率を向上させることができます。
背景
IDaaS は、標準 OIDC プロトコルに基づく認証コードメカニズムを使用して、企業のカスタム アプリケーションの SSO を実装します。 DingTalk や WeChat などのソーシャル ID プロバイダー(IdP)は、OAuth プロトコルを使用して QR コードベースのログインを実装します。 IDaaS で使用される OIDC プロトコルは、Open Authorization(OAuth)プロトコルのアップグレードバージョンです。
OAuth との互換性:OIDC 1.0 プロトコルは、OAuth 2.0 プロトコルの上にユーザー ID レイヤーを作成します。したがって、OIDC プロトコルは OAuth 2.0 プロトコルと互換性があります。 OIDC 認証コードフローは、OAuth 2.0 認証コードフローに似ています。違いは、OIDC 認証コードフローを使用する場合、ユーザー情報エンドポイントが標準化され、ユーザーの id トークンがトークンエンドポイントから返されることです。
認証コードフローの仕組み
カスタム アプリケーションの場合、SSO は OIDC 認証コードフローを使用して実装されます。
アプリケーションは、認証エンドポイントとトークンエンドポイントで IDaaS と対話するだけで、SSO のメインプロセスを完了できます。
ログインプロセスは IDaaS によって完全にホストされます。 アプリケーションはログイン結果を解析するだけで済みます。
SSO との統合
カスタム(または OIDC プロトコル)アプリケーションを作成する
IDaaS でカスタム アプリケーションまたは OIDC ベースのアプリケーションを作成し、アプリケーションキーを取得する必要があります。 アプリケーションキーを取得済みの場合は、この手順をスキップしてください。 詳細については、「カスタム アプリケーション」をご参照ください。
[全般] タブの [アプリケーション] ページで、client_id と client_secret を取得できます。 このキーのペアは、後続の API リクエストで使用されます。
キーを管理または交換する場合は、アプリケーションの 全般設定 を参照してください。
認証エンドポイントにリクエストを送信する
以下の手順は、アプリケーション開発者が処理する必要があります。
ユーザーがアプリケーションにアクセスしようとすると、アプリケーションはユーザーのログイン済み ID が使用可能かどうかを判断する必要があります。
ログインする必要がある場合は、IDaaS に認証ログインリクエストを送信する必要があります。 [アプリケーション] ページでアプリケーションをクリックし、[サインイン] タブを見つけて、[アプリケーション設定] セクションでアプリケーションの認証エンドポイントを取得します。
次の例に示すように、認証エンドポイント URL に基づいて完全な認証リクエスト URL を生成し、ブラウザで 302 リダイレクトを開始します。
{{Authorization Endpoint}}? client_id=app_***& redirect_uri=http%3A%2F%2Flocalhost%3A3000%2F***& response_type=code& scope=openid& state=525f49cc-***パラメーター
必須
例
説明
client_id
はい
app_michs7r****6pye
前の手順で取得した client_id。
scope
はい
openid email profile
デフォルトでは、カスタム アプリケーションの場合は openid email profile に設定されています。 これは、アプリケーションがログイン済みアカウントの ID、ユーザー名、およびメールアドレスを取得できることを示します。 OIDC ベースのアプリケーションを使用する場合、管理者は構成ページで Phone を選択することもできます。 scope が指定されたときにアプリケーションが取得することを承認されているパラメーター値の詳細については、このトピックの 3.2 アプリケーションがアクセスできるパラメーター値と指定された scope のマッピングを参照してください。
response_type
はい
code
このパラメーターの値は code として固定されています。 この値は、認証コードモードが使用されていることを示します。
redirect_uri
はい
http://localhost:3000/user/oauth2/aliyunidaas/callback
ユーザーがアプリケーションにログインした後に IDaaS からアプリケーションに返されるログイン結果のリダイレクト URL。 この URL は、アプリケーションが IDaaS パラメーターを受信するための中継アドレスであり、認証
codeを受信できます。state
いいえ
525f49cc-87c4-4655-b79c-4c4f971b1ad1
アプリケーションによって生成されたランダムな文字列。 長さが 32 文字以上の文字列を設定することをお勧めします。 state 値は、後続の手順でアプリケーションに返されます。 アプリケーションは、state 値がアプリケーションが認証エンドポイントに送信した初期値と同じかどうかを確認します。 これにより、クロスサイトリクエストフォージェリ(XSRF)セキュリティの脆弱性を防ぐために、リクエストが同じセッションからのものであることが保証されます。 このパラメーターに値を設定することをお勧めします。
セルフサービスログイン
認証エンドポイントに送信されたリクエストが成功すると、IDaaS ログインページにリダイレクトされます。
構成済みの任意のログイン方法で認証を完了できます。 IDaaS は、DingTalk QR コードログインや SMS ログインなど、さまざまなセキュリティレベルのさまざまなログイン機能を提供します。 詳細については、「全般設定」をご参照ください。
ログインに成功すると、ブラウザは URL パラメーターに code パラメーターと state パラメーターを使用して、アプリケーションによって指定された redirect_uri に 302 リダイレクトを実行します。
例:
{{redirect_uri}}? code=CO***& state=525f49cc-***パラメーター
例
説明
code
COE59pkCTm4A9nmowJUsfsfarGEaiShj3TuDc7NCzLCYu9
認証コード。アプリケーションは、アクセストークンの IDaaS に送信するリクエストで認証コードを使用します。
state
525f49cc-87c4-4655-b79c-4c4f971b1ad1
アプリケーションで受信した
state値が、アプリケーションが認証エンドポイントに送信した初期値と同じであることを確認します。トークンエンドポイントにリクエストを送信する
アプリケーションが前の手順で認証コードを受信した後、認証コードを使用してトークンエンドポイントに POST リクエストを送信します。
上記で説明した認証エンドポイントと同様に、[アプリケーション] ページでアプリケーションをクリックし、[サインイン] タブを見つけて、[アプリケーション設定] セクションでトークンエンドポイントを取得します。
サンプルリクエスト:
POST /v2/<instance_id>/<app_id>/oauth2/token HTTP/1.0 Host: eiam-api-cn-hangzhou.aliyuncs.com Content-Type: application/x-www-form-urlencoded grant_type=authorization_code &code=n0esc3N*****5acc3f0ogp4 &client_id=s6BhdR*****kqt3 &client_secret=7Fjfp0ZBr1*****KtDRbnfVdmIw &redirect_uri=http%3A%2F%2Fwww.example.com%2Fsso%2Fcallbackサンプルレスポンス:
{ "token_type": "Bearer", "access_token": "ATM4SoVDqWgUq***********wk3ZS5mtn6fcSp8NH8", "expires_in": 1200, "expires_at": 1644843164, "id_token": "eyJraWQiOiJLRVkyV************gRIadj-frOIRFChA" }この時点で、ユーザーはログインを正常に完了しました。 現在のログイン ID 情報をさらに取得し、アプリケーション側のログイン状態を作成するには、次の 2 つの方法のいずれかを選択できます。
レスポンス結果の id_token を使用し、検証後にユーザー ID を直接取得します。
レスポンス結果の access_token を使用して IDaaS ユーザーエンドポイントを呼び出し、現在ログインしているユーザー情報を取得します。
具体的な方法については、以下のセクションを参照してください。
重要取得できるユーザーデータの範囲は、認証エンドポイントリクエストの最初の手順の scope パラメーターで指定されます。
プログラムで id_token を解析する
id_tokenは、ID 情報を含む署名付きトークンです(JWT 形式)。 IDaaS によって発行された id_token には、デコード時にプレーンテキストで表示されるユーザーデータと署名が含まれています。理解を深めるために、完全な
id_tokenコンテンツを JWT デコード Web サイト に貼り付けて、そのコンテンツを表示できます。次のサンプルコードは例を示しています。
{ "kid": "KEY2Ty1qL6u21NGKmccv3jwfd2ndmgtQPnag", "alg": "RS256" }.{ "sub": "user_uyvefotjn7kpbejfmxoos3rtmm", "jti": "jwt_aaaac7xyhclac6aqkgtjaxsthw5yotn5d77pmki", "iss": "https://pre-eiam-api-cn-hangzhou.aliyun-inc.com/v2/********/oidc", "iat": 1644841965, "nbf": 1644841965, "exp": 1644842265, "aud": "app_mhylgo3iairjqjdx5eop6uaf34", "at_hash": "XHEaGpMooM9zvQXaMzCNEA", "name": "testuser", "preferred_username": "testuser", }.[Signature]アプリケーションログインにコンテンツを使用する前に、[署名] を検証して、トークンがサードパーティではなく IDaaS によって発行されたことを確認し、ログインのセキュリティを確保する必要があります。 このセキュリティ対策は必須です。
署名検証公開鍵を取得する
署名検証を実行する前に、最初に IDaaS によって公開された署名検証公開鍵エンドポイントを取得する必要があります。
[アプリケーション] ページでアプリケーションをクリックし、[サインイン] タブを見つけて、[アプリケーション設定] セクションで公開鍵エンドポイントを取得します。
アプリケーションは、このエンドポイントにアクセスすることで、現在の公開鍵情報を取得できます。 また、このアドレスをブラウザで直接開いて、公開鍵情報を表示することもできます。
次の例を参照してください。
{ "keys": [ { "kty": "RSA", "e": "AQAB", "use": "sig", "kid": "KEYkYnc55G********CTvT7So44RGDYdbfs", "n": "pXmYkIpy1vaNjTMclU86BQjfmDhjlqMAX8ySVvh9gO-nae4ayvG_*********-v4gP27T7u6bUy0GXTlh3eKE0v1LYB81nfqjF2uazlPwPR5yYOhhWcK-gMNByLfE3CnkDc1YGwA3dZmIz-ZjOCKy8xLaBuqjrvwn5tpMpAoYEEaH4jIm7unTdhbKEKspNR-UXKD8q9RppMh5Tn2sB6oPHlQANudJDgqSwEOevIrdmHU0Zqxrb9cscGH9hH0QjmYEu72yI8BVeliPo3jK6JIoqCIcj5K_t8BJlFQ9QLJ8_o9tmd3BFv5_LVsh4BKGw" } ] }これで、署名検証を完了し、
id_tokenのコンテンツを取得できます。署名を検証してログインする
JWT 公式ライブラリリスト で対応する言語ツールを見つけて、コード内のツールを使用して
id_tokenを検証および解析できます。次の例では、Java ライブラリ org.bitbucket.b_c:jose4j を使用しています。
最初に、対応する Maven 依存関係を追加します。
<dependency> <groupId>org.bitbucket.b_c</groupId> <artifactId>jose4j</artifactId> <version>0.7.12</version> </dependency>次のコードは例を示しています。
import org.jose4j.jwk.JsonWebKey; import org.jose4j.jwk.JsonWebKeySet; import org.jose4j.jwt.JwtClaims; import org.jose4j.jwt.consumer.JwtConsumer; import org.jose4j.jwt.consumer.JwtConsumerBuilder; public class IdTokenTest { public static void main(String[] args) throws Exception { // EIAM の発行者識別子(OIDC 発行者 URL) String issuer = "https://eiam-api-cn-hangzhou.aliyuncs.com/v2/idaas_padyrlux3mphrlsex4uonyqhxu/**********/oidc"; // 現在のアプリケーションの一意の識別子(EIAM から取得) String appId = "app_mkif4*****pxpzbasqmu"; // 解析用のアプリケーション公開鍵を次のように設定してください(JSON 形式、JWT 署名の検証に使用) String jwkJson = "{\n" + " \"keys\": [\n" + " {\n" + " \"kty\": \"RSA\",\n" + " \"e\": \"AQAB\",\n" + " \"use\": \"sig\",\n" + " \"kid\": \"KEY2H82C2at57itnW4onT3p1ySjwH4nirjCk\",\n" + " \"n\": \"w7Jl3fAUJp_9GuxV*****QsOA4lnXR5OD4kF4QbIeBiDiH8_MThrFi9k2MB6YMkSzf5JfIkpAS3JCqZ7k6Wooydp4pzaZNZAk3SGzdsa022RmAT" + "-Iayi4Yj6J9tSdTQCjwh2XkzzsIxA_Hla8rWiQ8Vhw1" + "-7QArgObfe67nSR7LxD55MFLxk9FU0*****RlGhrQGE_0LUuGWtCJG1r1e6aKquyswfxxAr3Rvj8QGIeJrG0R1Pv8m8d1_5OdULhB7149VqjM6D98WFjab0U2SNv0UlREZXTcS4p-2QNm_1egYRRpJEY_00FZqNSYsmErMGepYhO_61KoGqd8cphWQ\"\n" + " }\n" + " ]\n" + "}"; String jwt = "eyJraWQiOiJLRVkySDgyQzJhdD*****uaXJqQ2siLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c2Vy*****lmNjRjZjR3amFrbnBieGpjd3V1IiwianRpIjoiand0X2FhYWFkYWllYTc2eWg1cW0zcm11bnoyeGg0eHd5aTJzZHBoNjR6aSIsImlzcyI6Imh0dHBzOi8vZWlhbS1hcGktY24taGFuZ3pob3UuYWxpeXVuY3MuY29tL3YyL2lkYWFzX3BhZHlybHV4M21waHJsc2V4NHVvbnlxaHh1L2FwcF9ta2lmNGR3bHBlaDZkbnM0cHhwemJhc3FtdS9vaWRjIiwiaWF0IjoxNjUzNjMwMDQxLCJuYmYiOjE2NTM2MzAwNDEsImV4cCI6MTY1MzYzMDM0MSwiYXVkIjoiYXBwX21raWY0ZHdscGVoNmRuczRweHB6YmFzcW11IiwibmFtZSI6InRlc3QiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ0ZXN0IiwidXBkYXRlZF9hdCI6MTY1MzYyODU5MH0.pAsUNB8OkdpIxJMZRfLJ7Pa31tsJyl44a1jVIlvdQxwOtPULAwrFxnB0X3eQx89hUGCdvYWl9FO9o-5kT7L-RER0wJYz9YNKqrVNBnaRwINRZyeYLRVurWMMzODQz-V0ULd9raM1M_i2f_SoWFs1gPFtYh_ijUARHISi7Q3q93ZfAuY8Lq2Nq07QunmDbosvioUd5wJG7WCxW5XXZYDUQe9p5IEYd1MSvnWuTOLbg7rKn0Vm4dNYGWjz1WuoAyCsc_QxOCqpmQ_2czoqPeN-SvPJAQ2CykLk7DSnGpABw1aNrjDidLS9Beqsga9VDCth86sk_0lyTZOaORtUrfVTtQ"; // JWK 公開鍵セットを解析する JsonWebKeySet jsonWebKeySet = new JsonWebKeySet(jwkJson); // JWT バリデーターを作成する JwtConsumer jwtConsumer = createJwtConsumer(jsonWebKeySet, issuer, appId); // 署名検証を実行してクレームを解析する JwtClaims jwtClaims = jwtConsumer.processToClaims(jwt); // 署名検証が完了しました。 id_token に含まれるユーザー情報が出力されます。 System.out.println(jwtClaims); } // 署名検証ツールで使用されるメソッド public static JwtConsumer createJwtConsumer(JsonWebKeySet jsonWebKeySet, String issuer, String appId) { // ビルダーパターンを使用して JWT バリデーターを構成する final JwtConsumerBuilder jwtConsumerBuilder = new JwtConsumerBuilder(); // 必須フィールドの構成 jwtConsumerBuilder.setExpectedIssuer(issuer); jwtConsumerBuilder.setRequireIssuedAt(); jwtConsumerBuilder.setRequireExpirationTime(); jwtConsumerBuilder.setAllowedClockSkewInSeconds(60); jwtConsumerBuilder.setExpectedAudience(appId); // 公開鍵リゾルバーを設定する(kid で JWK を照合する) jwtConsumerBuilder.setVerificationKeyResolver((jws, nestingContext) - > { // JWT ヘッダーから kid を取得する final String signKeyId = jws.getKeyIdHeaderValue(); // JWK コレクションをトラバースして一致するキーを見つける for (JsonWebKey jsonWebKey: jsonWebKeySet.getJsonWebKeys()) { if (signKeyId.equals(jsonWebKey.getKeyId())) { return jsonWebKey.getKey(); } } throw new RuntimeException("検証キーが見つかりません: " + signKeyId); }); // バリデーターインスタンスをビルドする return jwtConsumerBuilder.build(); } }重要コードの issuer、appId、jwkJson、および jwt の部分を、EIAM から取得した実際の情報に置き換えてください。
出力の例:
JWT Claims Set:{sub=user_dt6kj6yf64cf4wjaknpbxjcwuu, jti=jwt_aaaadaiea76yh5qm3rmunz2xh4xwyi2sdph64zi, iss=https://eiam-api-cn-hangzhou.aliyuncs.com/v2/idaas_padyrlux3mphrlsex4uonyqhxu/app_**********/oidc, iat=1653630041, nbf=1653630041, exp=1653630341, aud=app_**********, name=test, preferred_username=test, updated_at=1653628590 }このようにして、アプリケーションは IDaaS でログオンしているユーザーの ID 情報を取得し、その情報を使用してログオンします。
UserInfo エンドポイントからユーザー情報を取得する
UserInfo エンドポイントからユーザー情報を取得することもできます。
[アプリケーション] ページでアプリケーションをクリックし、[サインイン] タブを見つけて、[アプリケーション設定] セクションで UserInfo エンドポイントを取得します。
ユーザー情報のリクエストは、標準の RFC6750 を使用します。サンプルリクエスト:
GET /v2/<instance_id>/<app_id>/oauth2/userinfo HTTP/1.0 Host: eiam-api-cn-hangzhou.aliyuncs.com Authorization: Bearer <AccessToken> レスポンスの例: { "sub": "user_dt6kj6yf64cf4wjaknpbxjcwuu", "name": "test", "preferred_username": "test", "updated_at": 1653899948 }説明UserInfo エンドポイントによって返される文字は、id_token のパラメーターと一致しています。これは、拡張 id_token で構成されたパラメーターも UserInfo エンドポイントから返されることを意味します。
詳細設定
OIDC プロトコルを深く理解している場合は、次の概念または機能を使用できます。
OIDC ディスカバリーエンドポイント
OIDC ベースのアプリケーションの
issuerは、トークン発行者(IDaaS)の一意の識別子です。 次の形式を使用します。https://<idaas-api-domain>/v2/<instance_id>/<application_id>/oidc次の表は、山括弧内のパラメーターについて説明しています。
パラメーター
説明
例
idaas-api-domain
ユーザーポータル URL
https://******.aliyunidaas.com
instance_id
インスタンス ID
idaas_m********r2ed22e6m
application_id
アプリケーション ID
app_m********jy6rbau
IDaaS は、OpenID Connect Discovery 1.0 標準をサポートしています。
/.well-known/openid-configurationをissuerの後に追加すると、アプリケーションの OIDC ディスカバリーエンドポイントアドレスが得られます。ディスカバリーエンドポイントにリクエストを送信することで、次のエンドポイント情報を検出できます。 すべてのリクエストエンドポイントは、[アプリケーション設定] から直接取得できます。
エンドポイント
説明
authorization_endpoint
承認エンドポイント
device_authorization_endpoint
デバイスモードでは、この機能の標準 OIDC アプリケーションサポートが必要です。 カスタム アプリケーションは現在、デバイスコードフローログインをサポートしていませんtoken_endpoint
トークンエンドポイント
revocation_endpoint
トークン失効エンドポイント
userinfo_endpoint
UserInfo エンドポイント
jwks_uri
JWK 公開鍵エンドポイント
scope とフィールド権限のマッピング
次の表に、scope が指定された場合の OIDC ベースのアプリケーションの id_token のユーザー情報を示します。
フィールド
scope
説明
sub
openid
ユーザーの
userIdjti
openid
JWT 形式のトークン。これは補助パラメーターです。
iss
openid
JWT の
issuer。 これは補助パラメーターです。iat
openid
JWT トークンが発行された時刻。これは補助パラメーターです。
nbf
openid
JWT トークンの有効期間の開始時刻。これは補助パラメーターです。
exp
openid
JWT トークンの有効期限。これは補助パラメーターです。
aud
openid
アプリケーションの ClientID。これは補助パラメーターです。
at_hash
openid
AccessToken のハッシュ値。これは補助パラメーターです。
phone_number
phone
電話番号(例:
+86 130****5678)phone_number_verified
phone
携帯電話番号が確認されているかどうか。デフォルトでは、携帯電話番号は確認されています。
email
email
メールアドレス(例:
al***@example.com)email_verified
email
メールアドレスが確認されているかどうか。デフォルトでは、メールアドレスは確認されています。
name
profile
ユーザーの表示名。
preferred_username
profile
ユーザーの
usernameupdated_at
profile
ユーザーのプロファイルが最後に更新された時刻。
トークンエンドポイントでサポートされている認証方法
OIDC プロトコルに従って、IDaaS は柔軟性を提供し、次の 4 つの認証方法をサポートしています。
ディスカバリーエンドポイントで返されるフィールド
token_endpoint_auth_methods_supportedは、サポートされている認証方法を指定します。値
説明
none
パブリッククライアントに使用されます。
none認証方法を使用して認証する場合、grant_typeをclient_credentialsにすることはできませんclient_secret_basic
この認証方法は、RFC 6749 - The OAuth 2.0 Authorization Framework に従って実装されています。
client_secret_post
この認証方法は、RFC 6749 - The OAuth 2.0 Authorization Framework に従って実装されています。
client_secret_jwt
この認証方法は、OpenID Connect Core 1.0 に従って実装されています。
アプリケーションが前の手順で認証コードを受信し、リクエストが有効であることを確認した後(リクエストを送信するときにアプリケーションが承認エンドポイントに送信した初期値と同じであることを state 値が確認した後)、アプリケーションは認証コードを使用してトークンエンドポイントに POST リクエストを送信します。
たとえば、
client_secret_basicを使用する場合、トークンエンドポイントリクエストの例は次のとおりです。POST /token HTTP/1.0 Host: api.aliyunidaas.com Authorization: Basic YXBwX21pY2hzN3I0*******cHllOkNTKioqKioq grant_type=authorization_code& code=COE59pkCTm4J*******arGEaiShj7NCzLCYu9詳細については、OIDC Core 1.0 の仕様を参照してください。
アプリケーションクライアントシークレットのローテーション
詳細については、「全般設定」のシークレットローテーションのセクションをご参照ください。