This topic provides an example on how to implement OpenID Connect (OIDC)-based single sign-on (SSO) from Okta to Alibaba Cloud. Then, applications that are registered in Okta can access Alibaba Cloud resources by using Security Token Service (STS) tokens in a secure manner.
Prerequisites
An OIDC application is registered in Okta. The URL of the issuer and the client ID of the application are obtained. The following data is used in this example:
The URL of the issuer is
https://dev-xxxxxx.okta.com
.The client ID is
0oa294vi1vJoClev****
.
Step 1: Create an OIDC identity provider (IdP) in Alibaba Cloud
In this step, an OIDC IdP named TestOidcProvider
is created. The URL of the issuer is https://dev-xxxxxx.okta.com
and the client ID is 0oa294vi1vJoClev****
.
Log on to the RAM console as a RAM administrator.
In the left-side navigation pane, choose .
On the Role-based SSO tab, click the OIDC tab. Then, click Add IdP.
On the Create IdP page, configure the following parameters.
Parameter
Description
IdP Name
The name must be unique within an Alibaba Cloud account.
Issuer URL
The URL of the issuer that is provided by an external IdP. The URL of the issuer must start with
https
and be a valid URL. The URL cannot contain query parameters that follow a question mark (?
), logon information that is identified by at signs (@
), or fragment that is identified by number signs (#
).Fingerprint
The fingerprint that is generated based on the HTTPS certificate of an external IdP. You can use a fingerprint to prevent the URL of the issuer from being hijacked or tampered with.
After you specify a valid value for Issuer URL, you can click Obtain Fingerprint. Alibaba Cloud calculates the fingerprint. We recommend that you calculate the fingerprint on your computer. For example, you can use OpenSSL to calculate the fingerprint. Then, you can compare the calculation result with the calculation result provided by Alibaba Cloud. For more information about OpenSSL, visit the official website of OpenSSL. If the calculation results are different, the URL of the issuer may have been attacked. Make sure that you enter a valid fingerprint.
NoteIf you want to rotate the certificate of your IdP, we recommend that you generate the fingerprint of the new certificate and add the fingerprint to the OIDC IdP that you created in the RAM console before the rotation. After at least one day, rotate the certificate. You can delete the previous fingerprint after you obtain a Security Token Service (STS) token.
Client IDs
The ID that is generated for an application when you register the application in the external IdP. When you apply for an OIDC token from an external IdP, you must use a client ID. The client ID is specified in the
aud
field of the OIDC token that is issued. When you create an OIDC IdP, you must configure the client ID. If you want to use the OIDC token to obtain an STS token, Alibaba Cloud checks whether the client ID that is specified in theaud
field is the same as the client ID that you configured in the OIDC IdP. You can assume a RAM role only when the client IDs are the same.If multiple clients need to access Alibaba Cloud resources, you can configure multiple client IDs. You can configure a maximum of 20 client IDs.
Earliest Issuance Time Allowed
The time limit on an OIDC token. If an OIDC token is issued earlier than the time limit, the OIDC token cannot be used to obtain an STS token.
Default value: 12 hours. Valid values: 1 to 168 hours.
Remarks
The description of the OIDC IdP.
Click OK.
Step 2: Create a RAM role for the OIDC IdP in Alibaba Cloud
In this step, a RAM role named testoidc
is created and the TestOidcProvider
OIDC IdP that you created in Step 1 is selected.
Log on to the RAM console as a RAM user who has administrative rights.
In the left-side navigation pane, choose .
On the Roles page, click Create Role.
On the Create Role page, select IdP in the Select Trusted Entity section and click Next.
Specify the RAM Role Name and Note parameters.
Select OIDC for the IdP Type parameter.
Select a trusted IdP and specify the conditions.
The following table describes the supported conditions.
Condition key
Description
Required
Examples
oidc:iss
The issuer. You can assume the RAM role only if the iss field of the OIDC token that you want to use to assume the RAM role meets this condition.
The conditional operator must be StringEquals. The value must be the URL of the issuer that you specify for the selected OIDC IdP. You can specify this condition to ensure that you can use the OIDC token to assume the RAM role only if the OIDC token is issued by a trusted IdP.
Yes
https://dev-xxxxxx.okta.com
oidc:aud
The audience. You can assume the RAM role only if the aud field of the OIDC token that you want to use to assume the RAM role meets this condition.
The conditional operator must be StringEquals. The value can be one or more client IDs that you specify for the selected OIDC IdP. You can specify this condition to ensure that you can use the OIDC token to assume the RAM role only if the OIDC token is generated by using the client ID that you specify.
Yes
0oa294vi1vJoClev****
oidc:sub
The subject. You can assume the RAM role only if the sub field of the OIDC token that you want to use to assume the RAM role meets this condition.
The conditional operator can be a string of all types. The value can be up to 10 subjects. You can specify this condition to further limit the identity that you can use to assume the RAM role. You can also leave this condition unspecified.
No
00u294e3mzNXt4Hi****
Click OK.
Click Close.
Step 3: Grant permissions to the RAM role
You can grant permissions to the RAM role named testoidc
that you created in Step 2 to access Alibaba Cloud resources based on your business requirements.
Log on to the RAM console as a RAM administrator.
In the left-side navigation pane, choose .
On the Roles page, find the RAM role that you want to manage and click Grant Permission in the Actions column.
You can also select multiple RAM roles and click Grant Permission in the lower part of the RAM role list to grant permissions to multiple RAM roles at a time.
In the Grant Permission panel, grant permissions to the RAM role.
Configure the Resource Scope parameter.
Account: The authorization takes effect on the current Alibaba Cloud account.
Resource Group: The authorization takes effect on a specific resource group.
NoteIf you select Resource Group for the Resource Scope parameter, make sure that the required cloud service supports resource groups. For more information, see Services that work with Resource Group.
Configure the Principal parameter.
The principal is the RAM role to which you want to grant permissions. The current RAM role is automatically selected.
Configure the Policy parameter.
A policy is a set of access permissions. You can select multiple policies at a time.
System policies: policies that are created by Alibaba Cloud. You can use but cannot modify these policies. Version updates of the policies are maintained by Alibaba Cloud. For more information, see Services that work with RAM.
NoteThe system automatically identifies high-risk system policies, such as AdministratorAccess and AliyunRAMFullAccess. We recommend that you do not grant unnecessary permissions by attaching high-risk policies.
Custom policies: You can manage and update custom policies based on your business requirements. You can create, update, and delete custom policies. For more information, see Create a custom policy.
Click Grant permissions.
Click Close.
Step 4: Issue an OIDC token in Okta
You cannot log on to the Alibaba Cloud Management Console by using OIDC. Therefore, you must implement OIDC-based SSO by using programmatic access. To obtain an OIDC token, you must complete authorization by using Open Authorization (OAuth). Therefore, you must use OAuth 2.0 to obtain an OIDC token from an OIDC IdP such as Okta. OAuth supports a variety of flows, such as the authorization code flow. For more information, see Authorization Code Flow. However, the authorization code flow is complex. In the following sections, the implicit flow is used to describe how to obtain an OIDC token and implement SSO. Some operations in the implicit flow are not described in this topic. For more information about the implicit flow, see Implicit Flow.
Build a web application to receive an OIDC token that is issued by Okta.
In this example, a simple web application that is built by using Java Spring Boot and Thymeleaf is used. The web application is deployed on your computer and is accessible over port 8080, and the localhost is resolved to 127.0.0.1. Therefore, you can enter localhost:8080 in a browser on your computer to access the web application. The following sample code is provided:
Sample code for a static page
OAuth 2.0 requires that the callback information that Okta sends to the web application is passed in the fragment component of the callback URL. You can create a web page and obtain the OIDC token from the fragment component. In this example, a simple static page is created. Then, you can transparently pass the fragment component. The complete URL of this page is
http://localhost:8080/accessTokenCallback
, which is also the callback URLredirect_uri
configured for the application in Okta.<!DOCTYPE HTML> <html xmlns:th="http://www.thymeleaf.org"> <head> <script> window.onload = function () { let fragment = window.location.hash.substring(1); window.location.href = "/receiveAccessToken?" + fragment; }; </script> </head> </html>
Sample code for a class
A class is created as the controller of the static page.
package com.aliyun.oauthtest; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class CallbackController { @RequestMapping("accessTokenCallback") public String callback() { return "accessTokenCallback"; } }
Log on to Okta and apply for an OIDC token from Okta.
You must log on to Okta. Then, you can construct and access the URL
https://dev-xxxxxx.okta.com/oauth2/v1/authorize?client_id=0oa294vi1vJoClev****&scope=openid&response_type=token%20id_token&state=testState&nonce=a_unique_nonce_1&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2FaccessTokenCallback
by using the web application that is built in Step 1.The following list describes the parameters in the URL:
client_id
: Set this parameter to the client ID of the OIDC application that is registered in Okta.scope
: Set this parameter toopenid
.response_type
: Set this parameter totoken id_token
in the implicit flow.state
: specifies the current status of the OIDC application. You can configure this parameter based on your business requirements.nonce
: This parameter is used to prevent replay attacks. You can configure this parameter based on your business requirements.redirect_uri
: Set this parameter to the callback URL that is used to receiveaccess_token
orid_token
. In this example, set this parameter to the URL of the web application that you created in Substep 1.
In this example, you have logged on to Okta. Therefore, the system redirects you to the callback URL based on the specified
redirect_uri
. The value ofid_token
in the following URL is the OIDC token.HTTP/1.1 302 Found Location: http://localhost:8080/accessTokenCallback#id_token=eyJraWQiOiJ6OUV0e****&access_token=eyJraWQiOiJseEQ3R****&token_type=Bearer&expires_in=3600&scope=openid&state=testState
Parse the OIDC token.
You can parse the results that you obtained in Substep 2 and query the details about
header
andpayload
.Sample request:
package com.aliyun.oauthtest; import java.util.Base64; import java.util.Base64.Decoder; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class ClientAppController { @RequestMapping(value = "/receiveAccessToken", method = {RequestMethod.POST, RequestMethod.GET}, produces = "application/json") public Map<String, Object> receiveAccessToken(@RequestParam("access_token") String accessToken, @RequestParam("id_token") String idToken, @RequestParam("token_type") String tokenType, @RequestParam("expires_in") Long expireTime, @RequestParam("scope") String scope, @RequestParam("state") String state) { Map<String, Object> result = new TreeMap<>(); result.put("access_token", accessToken); result.put("id_token", idToken); result.put("token_type", tokenType); result.put("expires_in", "" + expireTime); result.put("scope", scope); result.put("state", state); String[] jwt = idToken.split("\\."); Decoder decoder = Base64.getDecoder(); result.put(" id token jwt header", JSON.parse(new String(decoder.decode(jwt[0])))); result.put(" id token jwt payload", JSON.parse(new String(decoder.decode(jwt[1])))); result.put(" id token jwt signature", jwt[2]); return result; } }
Sample response:
{ " id token jwt header": { "kid": "z9EtyT345d-JLIJo2-5ySDO27LG4FPeOotbwJPT****", "alg": "RS256" }, " id token jwt payload": { "at_hash": "KKsdN3prZWTvBEMn-g****", "sub": "00u294e3mzNXt4Hi****", "aud": "0oa294vi1vJoClev****", "ver": 1, "idp": "0oa294iehxjUCZIO****", "amr": [ "pwd" ], "auth_time": 1636373097, "iss": "https://dev-xxxxxx.okta.com", "exp": 1636377759, "iat": 1636374159, "nonce": "a_unique_nonce_1", "jti": "ID.lmSU5AD2iKLCVu6_KLMIr52dpCprncxW38v-NCA****" }, "id token jwt signature": "ZEJEGIv4Zoau63****", "access_token": "eyJraWQiOiJseEQ3R****", "expires_in": "3600", "id_token": "eyJraWQiOiJ6OUV0e****", "scope": "openid", "state": "testState", "token_type": "Bearer" }
Step 5: Use the OIDC token to obtain an STS token
To obtain an STS token, call the AssumeRoleWithOIDC operation. In the request, specify the unparsed OIDC token that you obtained in Step 4.
Sample request:
public static void main(String[] args)
{
IAcsClient client = initialization();
String jwtToken = "eyJraWQiOiJ6OUV0e****"; //The unparsed OIDC token that you obtained from Okta. The token is the value of id_token.
AssumeRoleWithOIDCRequest request = new AssumeRoleWithOIDCRequest();
request.setDurationSeconds(3600L);
request.setOIDCProviderArn("acs:ram::113511544585****:oidc-provider/TestOidcProvider");
request.setOIDCToken(jwtToken);
request.setRoleArn("acs:ram::113511544585****:role/testoidc");
request.setRoleSessionName("TestOidcAssumedRoleSession");
try
{
AssumeRoleWithOIDCResponse resp = client.getAcsResponse(request);
System.out.println("success requestId: " + resp.getRequestId());
System.out.println("success assume role arn: " + resp.getAssumedRoleUser().getArn());
System.out.println("success sts credential accessKey id: " + resp.getCredentials().getAccessKeyId());
System.out.println("success sts credential accessKey secret: " + resp.getCredentials().getAccessKeySecret());
System.out.println("success resp: " + JSON.toJSONString(resp));
}
catch(ClientException | SystemException e)
{
e.printStackTrace();
}
}
Sample response:
success requestId: 3D57EAD2-8723-1F26-B69C-F8707D8B565D
success assume role arn: acs:ram::113511544585****:role/testoidc/TestOidcAssumedRoleSession
success sts credential accessKey id: STS.NUgYrLnoC37mZZCNnAbez****
success sts credential accessKey secret: CVwjCkNzTMupZ8NbTCxCBRq3K16jtcWFTJAyBEv2****
success resp:
{
"AssumedRoleUser":
{
"Arn": "acs:ram::113511544585****:role/testoidc/TestOidcAssumedRoleSession",
"AssumedRoleId": "33157794895460****:TestOidcAssumedRoleSession"
},
"Credentials":
{
"AccessKeyId": "STS.NUgYrLnoC37mZZCNnAbez****",
"AccessKeySecret": "CVwjCkNzTMupZ8NbTCxCBRq3K16jtcWFTJAyBEv2****",
"Expiration": "2021-10-20T04:27:09Z",
"SecurityToken": "CAIShwJ1q6Ft5B2yfSjIr****"
},
"OIDCTokenInfo":
{
"ClientIds": "0oa294vi1vJoClev****",
"Issuer": "https://dev-xxxxxx.okta.com",
"Subject": "00u294e3mzNXt4Hi****"
},
"RequestId": "3D57EAD2-8723-1F26-B69C-F8707D8B565D"
}
The information in Credentials
is the information about the STS token.
Step 6: Use the STS token to access Alibaba Cloud resources
Use the STS token that you obtained from Step 5 to access the Alibaba Cloud resources on which you have permissions.