Integrate SSO by OIDC

更新时间:
复制 MD 格式

IDaaS uses the OpenID Connect (OIDC) authorization code flow to implement single sign-on (SSO) for your custom applications. Your application interacts with two endpoints — the authorization endpoint and the token endpoint — to complete the full SSO flow. IDaaS hosts the entire authentication process; your application only needs to handle the result.

How it works

The following steps summarize the OIDC authorization code flow between your application, the user's browser, and IDaaS:

  1. Your application redirects the browser to the IDaaS authorization endpoint.

  2. IDaaS presents the sign-in page. The user authenticates.

  3. IDaaS redirects the browser back to your redirect_uri with an authorization code.

  4. Your application exchanges the code for tokens by calling the IDaaS token endpoint.

  5. Your application verifies the ID Token and establishes the user's session.

Note

OIDC 1.0 adds a user identity layer on top of OAuth 2.0 and is backward-compatible with the OAuth 2.0 authorization code flow. The key difference is that the token endpoint also returns an ID Token containing user identity claims.

Prerequisites

Before you begin, ensure that you have:

  • An IDaaS instance

  • A custom application or OIDC-based application created in IDaaS (see Custom applications)

  • The client_id and client_secret for your application (available on the General tab of the Applications page)

Integrate SSO with OIDC

Step 1: Get your application credentials

On the Applications page, click your application and open the General tab. Note the client_id and client_secret — these are required for all subsequent API requests.

To rotate or manage credentials, see General configurations.

Step 2: Send a request to the authorization endpoint

When a user accesses your application without an active session, redirect the browser to the IDaaS authorization endpoint with the following parameters.

Find the authorization endpoint URL on the Applications page: click your application, open the Sign-In tab, and look in the Application Settings section.

Construct the redirect URL as follows:

{{Authorization Endpoint}}?
  client_id=app_***&
  redirect_uri=http%3A%2F%2Flocalhost%3A3000%2F***&
  response_type=code&
  scope=openid&
  state=525f49cc-***
Parameter Required Example Description
client_id Yes app_michs7r****6pye The client ID from the General tab.
scope Yes openid email profile Controls which user attributes are returned. Default for custom applications: openid email profile. OIDC-based applications can also request phone. See Scope and field mapping.
response_type Yes code Must be code to use the authorization code flow.
redirect_uri Yes http://localhost:3000/user/oauth2/aliyunidaas/callback The URL where IDaaS sends the authorization code after the user signs in.
state No (recommended) 525f49cc-87c4-4655-b79c-4c4f971b1ad1 A random string you generate. IDaaS returns this value unchanged. Use it to verify the response comes from the same session and prevent Cross-Site Request Forgery (CSRF) attacks. Use at least 32 characters.
Important

Always validate the state value when IDaaS redirects back to your application. Reject any response where the returned state does not match the value you sent.

Step 3: Handle the sign-in callback

After the user authenticates, IDaaS performs a 302 redirect to your redirect_uri:

{{redirect_uri}}?
  code=CO***&
  state=525f49cc-***
Parameter Example Description
code COE59pkCTm4A9nmowJUsfsfarGEaiShj3TuDc7NCzLCYu9 The authorization code. Use it in the next step to request tokens.
state 525f49cc-87c4-4655-b79c-4c4f971b1ad1 Verify this value matches the one you sent in Step 2 before proceeding.

IDaaS supports multiple authentication methods, including DingTalk QR code and SMS. See General configuration for the full list.

Step 4: Exchange the authorization code for tokens

After validating the state value, exchange the authorization code for tokens by sending a POST request to the token endpoint. Find the token endpoint URL in the same Application Settings section as the authorization endpoint.

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

A successful response contains both an access token and an ID Token:

{
  "token_type": "Bearer",
  "access_token": "ATM4SoVDqWgUq***********wk3ZS5mtn6fcSp8NH8",
  "expires_in": 1200,
  "expires_at": 1644843164,
  "id_token": "eyJraWQiOiJLRVkyV************gRIadj-frOIRFChA"
}
Important

The user attributes available in the tokens are determined by the scope values you requested in Step 2.

To establish the user's session, use one of the following approaches:

Step 5: Parse and verify the ID Token

The ID Token is a JSON Web Token (JWT) signed with RS256. It contains user identity claims in plaintext, plus a Signature that you must verify before trusting the contents.

Important

Signature verification is mandatory. Do not use the claims in an ID Token without first verifying its signature.

To inspect the raw token structure, paste the id_token value into jwt.io. A decoded token looks like this:

{
  "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]

Get the public key for signature verification

Before verifying the signature, retrieve the JSON Web Key (JWK) public key from IDaaS. Find the public key endpoint URL in Application Settings (same location as the other endpoints).

Calling the endpoint returns a key set:

{
  "keys": [
    {
      "kty": "RSA",
      "e": "AQAB",
      "use": "sig",
      "kid": "KEYkYnc55G********CTvT7So44RGDYdbfs",
      "n": "pXmYkIpy1vaNjTMclU86BQjfmDhjlqMAX8ySVvh9gO-nae4ayvG_*********-v4gP27T7u6bUy0GXTlh3eKE0v1LYB81nfqjF2uazlPwPR5yYOhhWcK-gMNByLfE3CnkDc1YGwA3dZmIz-ZjOCKy8xLaBuqjrvwn5tpMpAoYEEaH4jIm7unTdhbKEKspNR-UXKD8q9RppMh5Tn2sB6oPHlQANudJDgqSwEOevIrdmHU0Zqxrb9cscGH9hH0QjmYEu72yI8BVeliPo3jK6JIoqCIcj5K_t8BJlFQ9QLJ8_o9tmd3BFv5_LVsh4BKGw"
    }
  ]
}

Verify the signature

Use a JWT library from the jwt.io library list to parse and verify the ID Token. The following example uses the Java library org.bitbucket.b_c:jose4j.

Add the Maven dependency:

<dependency>
  <groupId>org.bitbucket.b_c</groupId>
  <artifactId>jose4j</artifactId>
  <version>0.7.12</version>
</dependency>

Replace issuer, appId, jwkJson, and jwt with the actual values from your IDaaS application before running:

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's Issuer identifier (OIDC issuer URL)
        String issuer = "https://eiam-api-cn-hangzhou.aliyuncs.com/v2/idaas_padyrlux3mphrlsex4uonyqhxu/**********/oidc";
        // The unique identifier of the current application (obtained from EIAM)
        String appId = "app_mkif4*****pxpzbasqmu";
        // Application public key in JSON format, used to verify the JWT signature
        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";
        // Parse JWK public key set
        JsonWebKeySet jsonWebKeySet = new JsonWebKeySet(jwkJson);
        // Create JWT validator
        JwtConsumer jwtConsumer = createJwtConsumer(jsonWebKeySet, issuer, appId);
        // Execute signature verification and parse claims
        JwtClaims jwtClaims = jwtConsumer.processToClaims(jwt);
        // Prints the user information from the ID Token
        System.out.println(jwtClaims);
    }

    public static JwtConsumer createJwtConsumer(JsonWebKeySet jsonWebKeySet, String issuer, String appId) {
        // Configure JWT validator using builder pattern
        final JwtConsumerBuilder jwtConsumerBuilder = new JwtConsumerBuilder();
        jwtConsumerBuilder.setExpectedIssuer(issuer);
        jwtConsumerBuilder.setRequireIssuedAt();
        jwtConsumerBuilder.setRequireExpirationTime();
        jwtConsumerBuilder.setAllowedClockSkewInSeconds(60);
        jwtConsumerBuilder.setExpectedAudience(appId);
        // Set public key resolver (match JWK by kid)
        jwtConsumerBuilder.setVerificationKeyResolver((jws, nestingContext) -> {
            // Get kid from JWT header
            final String signKeyId = jws.getKeyIdHeaderValue();
            // Find the matching key in the JWK set
            for (JsonWebKey jsonWebKey : jsonWebKeySet.getJsonWebKeys()) {
                if (signKeyId.equals(jsonWebKey.getKeyId())) {
                    return jsonWebKey.getKey();
                }
            }
            throw new RuntimeException("Cannot find verification key: " + signKeyId);
        });
        return jwtConsumerBuilder.build();
    }
}

A successful run outputs the verified claims:

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
               }

Step 6: Get user information from the UserInfo endpoint

As an alternative to parsing the ID Token, use the access token to call the UserInfo endpoint. The endpoint follows RFC6750.

Find the UserInfo endpoint URL in Application Settings.

GET /v2/<instance_id>/<app_id>/oauth2/userinfo HTTP/1.0
Host: eiam-api-cn-hangzhou.aliyuncs.com
Authorization: Bearer <AccessToken>

Sample response:

{
    "sub": "user_dt6kj6yf64cf4wjaknpbxjcwuu",
    "name": "test",
    "preferred_username": "test",
    "updated_at": 1653899948
}
Note

The UserInfo endpoint returns the same fields as the ID Token, including any extended claims configured for the application.

Advanced settings

OIDC discovery endpoint

IDaaS supports the OpenID Connect Discovery 1.0 standard. The Issuer URL for an application uses the following format:

https://<idaas-api-domain>/v2/<instance_id>/<application_id>/oidc
Parameter Description Example
idaas-api-domain User portal domain ******.aliyunidaas.com
instance_id Instance ID idaas_m********r2ed22e6m
application_id Application ID app_m********jy6rbau

Append /.well-known/openid-configuration to the Issuer URL to get the discovery endpoint. Calling it returns all available endpoints:

Endpoint Description
authorization_endpoint Authorization endpoint
token_endpoint Token endpoint
userinfo_endpoint UserInfo endpoint
revocation_endpoint Token revocation endpoint
jwks_uri JWK public key endpoint
device_authorization_endpoint Device authorization endpoint (OIDC-based applications only; custom applications do not currently support device code flow login)

All endpoint URLs are also available directly in Application Settings.

Scope and field mapping

The following table shows which ID Token claims are returned for each scope value.

Field Scope Description
sub openid User ID
jti openid JWT ID (auxiliary)
iss openid Token issuer (auxiliary)
iat openid Token issue time (auxiliary)
nbf openid Token validity start time (auxiliary)
exp openid Token expiration time (auxiliary)
aud openid Application client ID (auxiliary)
at_hash openid Access token hash (auxiliary)
phone_number phone Phone number, e.g., +86 130****5678
phone_number_verified phone Whether the phone number is verified. Verified by default.
email email Email address, e.g., al***@example.com
email_verified email Whether the email address is verified. Verified by default.
name profile Display name
preferred_username profile Username
updated_at profile Last profile update time

Token endpoint authentication methods

IDaaS supports four authentication methods for the token endpoint, as specified in the token_endpoint_auth_methods_supported field of the discovery endpoint.

Method Description
none For public clients. Cannot be used with the client_credentials grant type.
client_secret_basic Per RFC 6749.
client_secret_post Per RFC 6749.
client_secret_jwt Per OpenID Connect Core 1.0.

The following example shows a token endpoint request using client_secret_basic:

POST /token HTTP/1.0
Host: api.aliyunidaas.com
Authorization: Basic YXBwX21pY2hzN3I0*******cHllOkNTKioqKioq

grant_type=authorization_code&
code=COE59pkCTm4J*******arGEaiShj7NCzLCYu9

Client secret rotation

See the secret rotation section in General configurations.

What's next

After completing the basic OIDC integration, you may want to:

References