Is your feature request related to a problem? Please describe.
Add AWS SSO as an identity provider for AWS.
AWS SSO AUTH FLOW
AWS SSO provides OAuth 2.0 Device Authorization Grant compatible APIs to enable command line tools to use AWS SSO to obtain AWS credentials. While those APIs are already documented and used by the AWS Command Line Interface version 2 (AWS CLI) they are not yet actively promoted by AWS.
The corresponding AWS APIs are:
With Leapp, we have to setup the integration with this specific Identity provider.
The thought is to provide an Integrations page to manage every external integration with Leapp.
So in the menu we will find and Integration button.
By clicking the integrations menu you will see an info to register AWS SSO as identity provider.
Whenever you click the AWS SSO button the auth flow must start.
The Auth flow in AWS SSO is made up with OIDC and is divided in three different steps:
- RegisterClient
- StartDeviceAuthorization
- CreateToken
CREDENTIALS ARE NOT NEEDED FOR THOSE THREE CALLS!
The first thing we have to do is to register a local client.
The AWS SSO OIDC service currently implements only the portions of the OAuth 2.0 Device Authorization Grant standard (https://tools.ietf.org/html/rfc8628) that are necessary to enable SSO authentication with the AWS CLI. Support for other OIDC flows frequently needed for native applications, such as Authorization Code Flow (+ PKCE), is planned in future releases.
RegisterClient
As soon as the button is started, the RegisterClient Api will be invoked.
aws sso-oidc register-client --client-name leapp --client-type public
NOTES: public is the only client type available at the moment.
it will generate a response like the one below:
{
"clientId": "Clhq.....tMQ",
"clientSecret": "eyJra.....wi...lK",
"clientIdIssuedAt": 1603898686,
"clientSecretExpiresAt": 1611674686
}
By decoding the Client secret you will see:
{
"expired": false,
"clientId": {
"value": "R-j5yN-4-TPNs...tMQ"
},
"clientName": "my-client",
"clientType": "PUBLIC",
"templateArn": null,
"templateContext": null,
"expirationTimestamp": 1594932567.612,
"createdTimestamp": 1587156567.612,
"updatedTimestamp": 1587156567.612,
"createdBy": null,
"updatedBy": null
}
We have to locally save those info and pass them into the next call
StartDeviceAuthorization
in order to make this call the user of Leapp need to have registered into AWS SSO and provide a valid start-url, that is the portal url
Having a valid OIDC client registration we can now initiate the device authorization flow using the StartDeviceAuthorization API action.
aws sso-oidc start-device-authorization
--client-id mVZBEoa-gUBj8nugHZUIsWV1LXdlc3QtMQ
--client-secret eyJra...a_tV-htKetQH8
--start-url ENDPOINTURL
That will produce a response like this:
{
"deviceCode": "8Acq...DUg",
"expiresIn": 600,
"interval": 1,
"userCode": "RPXP-JSQA",
"verificationUri": "https://device.sso.eu-central-1.amazonaws.com/",
"verificationUriComplete": "https://device.sso.eu-central-1.amazonaws.com/?user_code=RPXP-JSQA"
}
Where:
- deviceCode: The short-lived code that is used by the device when polling for a session token.
- expiresIn: Indicates the number of seconds in which the verification code will become invalid.
- interval: Indicates the number of seconds the client must wait between attempts when polling for a session.
- userCode: A one-time user verification code. This is needed to authorize an in-use device.
- verificationUri: The URI of the verification page that takes the
userCode
to authorize the device.
- **verificationUriComplete:**An alternate URL that the client can use to automatically launch a browser. This process skips the manual step in which the user visits the verification page and enters their code.
In order to verify this device, Leapp will prompt you to the LOGIN PAGE and the verification code page, by opening a web browser with the verificationUriComplete
After the verification of the device we can now call the CreateToken
API
CreateToken
Having the userCode
and deviceCode
values we can now us the CreateToken API action to obtain a device access token. However, before we can request any tokens we need to open the verificationUriComplete
URL in a web browser, complete authentication and provide authorization. At this point the end-user has to provide personal credentials in order to authenticate himself, for example by entering his username, password and maybe also has to provide multi factory authentication (MFA) credentials.
curl -X POST https://oidc.eu-central-1.amazonaws.com/token -d
'{"clientId": "R-j5yN-4-TPNs...tMQ", "clientSecret": "eyJr...x74", "deviceCode": "8Acq...DUg", "grantType": "urn:ietf:params:oauth:grant-type:device_code"}'
If everything worked out as expected, the token endpoint will return a valid accessToken, which we later can use with the AWS SSO Portal API to obtain temporary AWS credentials using the GetRoleCredentials action.
The returned tokens are valid for 8 hours.
This API call is region sensible, so we have to extract the region from the verificationUriComplete
url with a RegEx!
With this token we can now call the AWS SSO Portal API to autogenerate sessions in Leapp.
AWS SSO Portal API
In order to fill the session available with AWS SSO we have to use those APIs
The following actions are supported:
and in order to complete this action we need scan for account and roles inside them, so it's a time consuming moment.
so a loading modal will appear:
What happens whenever this modal will appear?
ListAccounts
Firstly we have to check all the accounts available for the current user.
Lists all AWS accounts assigned to the user. These AWS accounts are assigned by the administrator of the account.
GET /assignment/accounts?max_result=maxResults&next_token=nextToken HTTP/1.1
x-amz-sso_bearer_token: accessToken
- accessToken: The token issued by the
CreateToken
API call. Required: Yes
- maxResults: This is the number of items clients can request per page. Valid Range: Minimum value of 1. The maximum value of 100.
- nextToken: When requesting subsequent pages, this is the page token from the previous response output. Required: NO
RESPONSE
{
"accountList": [
{
"accountId": "string",
"accountName": "string",
"emailAddress": "string"
}
],
"nextToken": "string"
}
The following data is returned in JSON format by the service.
- accountList: A paginated response with the list of account information and the next token if more results are available.
- accountId: The identifier of the AWS account that is assigned to the user.
- accountNameThe display name of the AWS account that is assigned to the user.
- emailAddressThe email address of the AWS account that is assigned to the user. Length
For each account retrieved by this API, we need to check all the possible roles inside it.
aws sso list-account-roles --access-token eyJlbmMiOiJBM...nZEpteo-dHw --account-id 198863347786
Response:
{
"roleList": [
{
"roleName": "ViewOnlyAccess",
"accountId": "198000000086"
}
]
}
After that for each pair of Account and role found we will add an AWS session that is available to get the correct credentials.
in order to retrieve credentials we have to make this call
GetRoleCredentials
Having a valid access token, we can use the AWS SSO Portal API to obtain temporary AWS credentials using the GetRoleCredentials action.
curl 'https://portal.sso.eu-central-1.amazonaws.com/federation/credentials?account_id=999999999999&role_name=MyIamRoleName' -H 'x-amz-sso_bearer_token: eyJl...Blw'
Which will then provide us with temporary AWS session credentials, which by default are valid for 12 hours.
{
"roleCredentials": {
"accessKeyId": "ASIA...Z3XM",
"secretAccessKey": "+t6UnVLWia...hFOzGL",
"sessionToken": "IQoJb3JpZ2lu...dK6",
"expiration": 150000008000
}
}
I tried several additional query parameters (session_duration
, duration_seconds
, …) to figure out whether one can choose a shorter session duration, but it looks like 12 hours is all you can get. While AWS STS API’s actions like AssumeRole or AssumeRoleWithWebIdentity allow callers to specify custom session duration, GetRoleCredentials does not yet support that.
Final thoughts
there are still two actions to be implemented in the App, the Logout
action and the Sync
action.
The first call the api call of the portal
The second one will synchronize the session with the current configuration.