使用OIDC进行角色SSO的示例

本文提供一个Okta与阿里云进行OIDC角色SSO的示例,使Okta中的应用通过临时身份凭证(STS Token)安全访问阿里云资源。

前提条件

请提前在Okta中注册一个OIDC应用,并获取应用的颁发者URL和客户端ID(Client ID)。本示例中使用的数据如下:

  • 颁发者URL:https://dev-xxxxxx.okta.com

  • 客户端ID:0oa294vi1vJoClev****

步骤一:在阿里云创建OIDC身份提供商

本步骤中将创建一个名为TestOidcProviderOIDC身份提供商。颁发者URLhttps://dev-xxxxxx.okta.com客户端ID0oa294vi1vJoClev****

  1. 使用RAM管理员登录RAM控制台

  2. 在左侧导航栏,选择集成管理 > SSO管理

  3. 角色SSO页签,先单击OIDC页签,然后单击创建身份提供商

  4. 创建身份提供商页面,设置身份提供商信息。

    参数

    说明

    身份提供商名称

    同一个阿里云账号下必须唯一。

    颁发者URL

    颁发者URL由外部IdP提供。颁发者URL必须以https开头,符合标准URL格式,但不允许带有query参数(以?标识)、fragment片段(以#标识)和登录信息(以@标识)。

    验证指纹

    为了防止颁发者URL被恶意劫持或篡改,您需要配置外部IdPHTTPS CA证书生成的验证指纹。

    填写完颁发者URL后,您可以单击获取指纹,阿里云会辅助您自动计算出验证指纹,但是建议您在本地自己计算一次(例如:通过OpenSSL获取OIDC IdP的指纹),与阿里云计算的指纹进行对比。如果对比发现不同,则说明该颁发者URL可能已经受到攻击,请您务必再次确认,并填写正确的指纹。

    说明

    当您的IdP计划进行证书轮转时,请在轮转前生成新证书的指纹并添加到阿里云OIDC身份提供商信息中,一段时间(至少一天)以后再进行证书轮转,证书轮转确认可以换取到STS Token后再删除旧的指纹。

    客户端ID

    您的应用在外部IdP注册的时候,会生成一个客户端ID(Client ID)。当您从外部IdP申请签发OIDC令牌(OIDC Token)时必须使用该客户端ID,签发出来的OIDC Token也会通过aud字段携带该客户端ID。在创建OIDC身份提供商时配置该客户端ID,然后在使用OIDC Token换取STS Token时,阿里云会校验OIDC Tokenaud字段所携带的客户端IDOIDC身份提供商中配置的客户端ID是否一致。只有一致时,才允许扮演角色。

    如果您有多个应用需要访问阿里云,您可以配置多个客户端ID,但最多不能超过50个。

    最早颁发时间限制

    在该限制时间之前颁发的OIDC Token不允许换取STS Token。

    默认值:12小时。取值范围:1~168小时。

    备注

    身份提供商的描述信息。

  5. 单击创建身份提供商

步骤二:在阿里云创建可信实体为OIDC身份提供商的RAM角色

本步骤中将创建一个名为testoidcRAM角色,身份提供商选择步骤一创建的TestOidcProvider

  1. 使用RAM管理员登录RAM控制台

  2. 在左侧导航栏,选择身份管理 > 角色

  3. 角色页面,单击创建角色

    image

  4. 创建角色页面的右上角,单击切换编辑器

    image

  5. 在编辑器中指定具体的OIDC身份提供商。

    编辑器支持可视化编辑和脚本编辑两种模式,您可以任选其一。

    • 可视化编辑

      主体中指定具体的OIDC身份提供商。

      image

      image

    • 脚本编辑

      PrincipalFederated字段中指定具体的OIDC提供商,同时配置Condition

      {
        "Version": "1",
        "Statement": [
          {
            "Effect": "Allow",
            "Principal": {
              "Federated": "acs:ram::100*******0719:oidc-provider/xiyun****"
            },
            "Action": "sts:AssumeRole",
            "Condition": {
              "StringEquals": {
                "oidc:iss": [
                  "https://dev-xxxxxx.okta.com"
                ],
                "oidc:aud": [
                  "0oa294vi1vJoClev****"
                ]
              }
            }
          }
        ]
      }
  6. 在编辑器中设置限制条件。

    支持的服务级限制条件如下表所示:

    限制条件关键字

    说明

    是否必选

    示例

    oidc:iss

    OIDC颁发者(Issuer)。用来扮演角色的OIDC令牌中的iss字段值必须满足该限制条件要求,角色才允许被扮演。

    该限定条件必须使用StringEquals作为条件操作类型,条件值只能是您在OIDC身份提供商中填写的颁发者URL。该限制条件用于确保只有受信颁发者颁发的OIDC令牌才能扮演角色。

    https://dev-xxxxxx.okta.com

    oidc:aud

    OIDC受众(Audience)。用来扮演角色的OIDC令牌中的aud字段值必须满足该限制条件要求,角色才允许被扮演。

    该限定条件必须使用StringEquals作为条件操作类型,您可选择在OIDC身份提供商中配置的一个或多个客户端ID(Client ID)作为条件值。该限制条件用于确保只有您设置的Client ID生成的OIDC令牌才能扮演角色。

    0oa294vi1vJoClev****

    oidc:sub

    OIDC主体(Subject)。用来扮演角色的OIDC令牌中的sub字段值必须满足该限制条件要求时,角色才允许被扮演。

    该限定条件可以使用任何String类的条件操作类型,且您可以最多设置10OIDC主体作为条件值。该限制条件用于进一步限制允许扮演角色的身份主体,您也可以不指定该限制条件。

    00u294e3mzNXt4Hi****

  7. 创建角色对话框,输入角色名称,然后单击确定

步骤三:为RAM角色授权

您可以根据实际需要,为步骤二创建的RAM角色testoidc授予访问阿里云资源的权限。

  1. 使用RAM管理员登录RAM控制台

  2. 在左侧导航栏,选择身份管理 > 角色

  3. 角色页面,单击目标RAM角色操作列的新增授权

    image

    您也可以选中多个RAM角色,单击角色列表下方的新增授权,为RAM角色批量授权。

  4. 新增授权面板,为RAM角色授权。

    1. 选择资源范围。

      • 账号级别:权限在当前阿里云账号内生效。

      • 资源组级别:权限在指定的资源组内生效。

        说明

        指定资源组授权生效的前提是该云服务及资源类型已支持资源组,详情请参见支持资源组的云服务

    2. 选择授权主体。

      授权主体即需要添加权限的RAM角色。系统会自动选择当前的RAM角色。

    3. 选择权限策略。

      权限策略是一组访问权限的集合。支持批量选中多条权限策略。

      • 系统策略:由阿里云创建,策略的版本更新由阿里云维护,用户只能使用不能修改。更多信息,请参见支持RAM的云服务

        说明

        系统会自动标识出高风险系统策略(例如:AdministratorAccess、AliyunRAMFullAccess等),授权时,尽量避免授予不必要的高风险权限策略。

      • 自定义策略:由用户管理,策略的版本更新由用户维护。用户可以自主创建、更新和删除自定义策略。更多信息,请参见创建自定义权限策略

    4. 单击确认新增授权

  5. 单击关闭

步骤四:在Okta签发OIDC令牌(OIDC Token)

阿里云不支持使用OIDC登录控制台,所以您需要使用程序访问的方式完成OIDC SSO流程。由于生成OIDC Token本质上是个OAuth流程,所以您需要通过标准的OAuth 2.0流程从OIDC IdP(例如:Okta)获取OIDC Token。OAuth支持多种流程,例如:比较常见的Authorization Code Flow

下文以Okta官方教程使用AuthJS登录单页应用(SPA)为例,演示如何从Okta获取OIDC Token。

  1. 按照Okta的教程创建应用并完成项目配置。

  2. 在浏览器中测试项目,应用将自动重定向到Okta登录页面。完成登录和MFA认证后,页面将重定向至index.html。页面会显示当前登录用户的ID Token及其解析后的Claims信息。如下所示:

    {
      "idToken": "eyJraWQiOiItbUF****",
      "claims": {
        "sub": "00uxbq0z40UYy9bm****",
        "name": "ssotest01",
        "email": "ssotest01@exampledomain.com",
        "ver": 1,
        "iss": "https://dev-xxxxxx.okta.com",
        "aud": "0oaxbqhfrfBl5lk2****",
        "iat": 1762841679,
        "exp": 1762845279,
        "jti": "ID.WYtCLmLKOlMcEh0uIe1jWH9T6M1JmotCvX3hIgLK6mA",
        "amr": [
          "mfa",
          "otp",
          "pwd",
          "okta_verify"
        ],
        "idp": "00oxbpgns1TnfLFg****",
        "nonce": "Xp0PTyQzw9ltYBY7SfhxG2ijt1wgi2jK6XLZOGbeQJQ79d0ScWYoHE5twl0QAklA",
        "preferred_username": "ssotest01@exampledomain.com",
        "auth_time": 1762841677,
        "at_hash": "wztv8ALAo2Au56Om3dya7w"
      },
      "expiresAt": 1762845279,
      "scopes": [
        "openid",
        "profile",
        "email"
      ],
      "authorizeUrl": "https://dev-xxxxxx.okta.com/oauth2/v1/authorize",
      "issuer": "https://dev-xxxxxx.okta.com",
      "clientId": "0oaxbqhfrfBl5lk2****"
    }
    说明

    在页面展示的claims信息中,找到以下属性,并确保它们与在步骤一:在阿里云创建OIDC身份提供商中配置的OIDC身份提供商信息匹配,否则在调用AssumeRoleWithOIDC API时将会失败。

    • iss:必须与颁发者URL完全匹配

    • aud:必须与客户端ID完全匹配

  3. 复制页面中所展示的idToken并保存。

步骤五:使用OIDC Token换取STS Token

您可以直接调用AssumeRoleWithOIDC API,使用从步骤四中获取的OIDC Token(ID token)换取STS Token。

请求示例:

package demo;
import com.aliyun.auth.credentials.provider.AnonymousCredentialProvider;
import com.aliyun.sdk.service.sts20150401.models.*;
import com.aliyun.sdk.service.sts20150401.*;
import com.google.gson.Gson;
import darabonba.core.client.ClientOverrideConfiguration;
import java.util.concurrent.CompletableFuture;


public class AssumeRoleWithOIDC {
  public static void main(String[] args) throws Exception {

    // Anonymous access method (requires API support)
    AnonymousCredentialProvider provider = AnonymousCredentialProvider.create();

    // Configure the Client
    AsyncClient client = AsyncClient.builder()
      .region("cn-hangzhou") // Region ID
      .credentialsProvider(provider)
      // Client-level configuration rewrite, can set Endpoint, Http request parameters, etc.
      .overrideConfiguration(
        ClientOverrideConfiguration.create()
        // Endpoint 请参考 https://api.aliyun.com/product/Sts
        .setEndpointOverride("sts.cn-hangzhou.aliyuncs.com")
      )
      .build();

    String idToken = "eyJraWQiOiItbUF****"; // OIDC id token
    // Parameter settings for API request
    AssumeRoleWithOIDCRequest assumeRoleWithOIDCRequest = AssumeRoleWithOIDCRequest.builder()
      .OIDCToken(idToken)
      .roleArn("acs:ram::173305794806****:role/testoidc")
      .OIDCProviderArn("acs:ram::173305794806****:oidc-provider/Okta")
      .roleSessionName("test-oidc-session")
      .build();
    // Asynchronously get the return value of the API request
    CompletableFuture<AssumeRoleWithOIDCResponse> response = client.assumeRoleWithOIDC(assumeRoleWithOIDCRequest);
    // Synchronously get the return value of the API request
    AssumeRoleWithOIDCResponse resp = response.get();
    System.out.println("RequestId: " + resp.getBody().getRequestId());
    System.out.println("Assume role ARN: " + resp.getBody().getAssumedRoleUser().getArn());
    System.out.println("Credentials AccessKeyId: " + resp.getBody().getCredentials().getAccessKeyId());
    System.out.println("Credentials AccessKeySecret: " + resp.getBody().getCredentials().getAccessKeySecret());
    System.out.println("Success response: " + new Gson().toJson(resp.getBody()));

    // Finally, close the client
    client.close();
  }

}

返回示例:

RequestId: 5EB6E605-15EA-5D96-941D-2C62DC99****
Assume role ARN: acs:ram::173305794806****:role/testoidc/test-oidc-session
Credentials AccessKeyId: STS.NZXMTBGTTHLW74AMVYKou****
Credentials AccessKeySecret: FdrYjJwTCfFfMdY4APdHsjGL9fH5RSZCtRyYMJED****
Success response:
{
    "assumedRoleUser": {
        "arn": "acs:ram::173305794806****:role/testoidc/test-oidc-session",
        "assumedRoleId": "30048007026011****:test-oidc-session"
    },
    "credentials": {
        "accessKeyId": "STS.NZXMTBGTTHLW74AMVYKou****",
        "accessKeySecret": "FdrYjJwTCfFfMdY4APdHsjGL9fH5RSZCtRyYMJED****",
        "expiration": "2025-11-11T08:01:03Z",
        "securityToken": "CAISwwJ1q6Ft5B2yfSjI****"
    },
    "OIDCTokenInfo": {
        "clientIds": "0oaxbqhfrfBl5lk2****",
        "expirationTime": "2025-11-11T07:14:39Z",
        "issuanceTime": "2025-11-11T06:14:39Z",
        "issuer": "https://dev-xxxxxx.okta.com",
        "subject": "00uxbq0z40UYy9bm****",
        "verificationInfo": "Success"
    },
    "requestId": "5EB6E605-15EA-5D96-941D-2C62DC99****"
}

其中Credentials中的信息即为STS Token。

步骤六:使用STS Token访问阿里云资源

使用从步骤五获取的STS Token访问有权限的阿里云资源。