Using Amazon Verified Permissions with identity providers - Amazon Verified Permissions

Using Amazon Verified Permissions with identity providers

An identity source is a representation of an external identity provider (IdP) in Amazon Verified Permissions. Identity sources provide information from a user who authenticated with an IdP that has a trust relationship with your policy store. When your application makes an authorization request with a token from an identity source, your policy store can make authorization decisions from user properties and access permissions. Verified Permissions identity sources improve authorization with a direct connection to your central identity store and authentication service.

You can use OpenID Connect (OIDC) identity providers (IdPs) with Verified Permissions. Your application can generate authorization requests with OIDC identity (ID) or access JSON web tokens (JWTs). With ID tokens, Verified Permissions reads user IDs and attributes claims as principals for attribute-based access control (ABAC). With access tokens, Verified Permissions reads user IDs as principals and other claims as context. With both token types, you can map a claim like groups to a principal group, and build policies that evaluate role-based access control (RBAC).

You can add an Amazon Cognito user pool or a custom OpenID Connect (OIDC) IdP as your identity source.

Working with Amazon Cognito identity sources

Verified Permissions works closely with Amazon Cognito user pools. Amazon Cognito JWTs have a predictable structure. Verified Permissions recognizes this structure and draws maximum benefit from the information that it contains. For example, you can implement an role-based access control (RBAC) authorization model with either ID tokens or access tokens.

A new Amazon Cognito user pools identity source requires the following information:

  • The AWS Region.

  • The user pool ID.

  • The user entity type that you want to associate with your identity source, for example MyCorp::User.

  • The group entity type that you want to associate with your identity source, for example MyCorp::UserGroup.

  • (Optional) The client IDs from your user pool that you want to authorize to make requests to your policy store.

Because Verified Permissions only works with Amazon Cognito user pools in the same AWS account, you can't specify an identity source in another account. Verified Permissions sets the entity prefix—the identity-source identifier that you must reference in policies that act on user pool principals—to the ID of your user pool, for example us-west-2_EXAMPLE.

User pool token claims can contain attributes, scopes, groups, client IDs, and custom data. Amazon Cognito JWTs have the ability to include a variety of information that can contribute to authorization decisions in Verified Permissions. These include:

  1. Username and group claims with a cognito: prefix

  2. Custom user attributes with a custom: prefix

  3. Custom claims added at runtime

  4. OIDC standard claims like sub and email

We cover these claims in detail, and how to manage them in Verified Permissions policies, in Working with identity sources in schema and policies.

Important

Although you can revoke Amazon Cognito tokens before they expire, JWTs are considered to be stateless resources that are self-contained with a signature and validity. Services that conform with the JSON Web Token RFC 7519 are expected to validate tokens remotely and aren't required to validate them with the issuer. This means that it is possible for Verified Permissions to grant access based on a token that was revoked or issued for user that was later deleted. To mitigate this risk, we recommend that you create your tokens with the shortest possible validity duration and revoke refresh tokens when you want to remove authorization to continue a user's session.

Cedar policies for user pool identity sources in Verified Permissions use a special syntax for claim names that contain characters other than alphanumeric and underscore (_). This includes user pool prefix claims that contain a : character, like cognito:username and custom:department. To write a policy condition that references the cognito:username or custom:department claim, write them as principal["cognito:username"] and principal["custom:department"], respectively.

Note

If a token contains a claim with a cognito: or custom: prefix and a claim name with the literal value cognito or custom, an authorization request with IsAuthorizedWithToken will fail with a ValidationException.

This example shows how you might create a policy that references some of the Amazon Cognito user pools claims associated with a principal.

permit( principal == ExampleCo::User::"us-east-1_example|4fe90f4a-ref8d9-4033-a750-4c8622d62fb6", action, resource == ExampleCo::Photo::"VacationPhoto94.jpg" ) when { principal["cognito:username"]) == "alice" && principal["custom:department"]) == "Finance" };

For more information about mapping claims, see Mapping ID tokens to schema. For more information about authorization for Amazon Cognito users, see Authorization with Amazon Verified Permissions in the Amazon Cognito Developer Guide.

Working with OIDC identity sources

You can also configure any compliant OpenID Connect (OIDC) IdP as the identity source of a policy store. OIDC providers are similar to Amazon Cognito user pools: they produce JWTs as the product of authentication. To add an OIDC provider, you must provide an issuer URL

A new OIDC identity source requires the following information:

  • The issuer URL. Verified Permissions must be able to discover a .well-known/openid-configuration endpoint at this URL.

  • The token type that you want to use in authorization requests. In this case, you chose Identity token.

  • The user entity type that you want to associate with your identity source, for example MyCorp::User.

  • The group entity type that you want to associate with your identity source, for example MyCorp::UserGroup.

  • An example ID token, or a definition of the claims in the ID token.

  • The prefix that you want to apply to user and group entity IDs. In the CLI and API, you can choose this prefix. In policy stores that you create with the Set up with API Gateway and an identity source or Guided setup option, Verified Permissions assigns a prefix of the issuer name minus https://, for example MyCorp::User::"auth.example.com|a1b2c3d4-5678-90ab-cdef-EXAMPLE11111".

Authorization with OIDC identity sources uses the same API operations as user pool identity sources: IsAuthorizedWithToken and BatchIsAuthorizedWithToken.

This example shows how you might create a policy that permits access to year-end reports for employees in the accounting department, have a confidential classification, and aren't in a satellite office. Verified Permissions derives these attributes from the claims in the principal's ID token.

permit( principal in MyCorp::UserGroup::"MyOIDCProvider|Accounting", action, resource in MyCorp::Folder::"YearEnd2024" ) when { principal.jobClassification == "Confidential" && !(principal.location like "SatelliteOffice*") };

Client and audience validation

When you add an identity source to a policy store, Verified Permissions has configuration options that verify that ID and access tokens are being used as intended. This validation happens in the processing of IsAuthorizedWithToken and BatchIsAuthorizedWithToken API requests. The behavior differs between ID and access tokens, and between Amazon Cognito and OIDC identity sources. With Amazon Cognito user pools providers, Verified Permissions can validate the client ID in both ID and access tokens. With OIDC providers, Verified Permissions can validate the client ID in ID tokens, and the audience in access tokens.

A client ID is an identifier associated with an OAuth or OIDC application that's configured with the provider, for example 1example23456789. An audience is a URL path associated with the intended relying party, or destination, of the target application, for example https://myapplication.example.com. The aud claim isn't always associated with the audience.

Verified Permissions performs identity source audience and client validation as follows:

Amazon Cognito

Amazon Cognito ID tokens have an aud claim that contains the app client ID. Access tokens have a client_id claim that also contains the app client ID.

When you enter one or more values for Client application validation in your identity source, Verified Permissions compares this list of app client IDs to the ID token aud claim or the access token client_id claim. Verified Permissions doesn't validate a relying-party audience URL for Amazon Cognito identity sources.

OIDC

OIDC ID tokens have an aud claim that contains a list of client IDs. Access tokens have an aud claim that contains the audience URL for the token. Access tokens also have a client_id claim that contains the intended client ID.

You can enter one or more values for Audience validation with an OIDC provider. When you choose a Token type of ID token, Verified Permissions validates the client ID, checking that at least one member of the client IDs in the aud claim matches an audience validation value.

Verified Permissions validates the audience for access tokens, checking that the aud claim matches an audience validation value. This access-token value primarily comes from the aud claim but can come from the cid or client_id claim if no aud claim exists. Check with your IdP for the correct audience claim and format.

An example ID token audience validation value is 1example23456789.

An example access token audience validation value is https://myapplication.example.com.

Client-side authorization for JWTs

You might want to process JSON web tokens in your application and pass their claims to Verified Permissions without using a policy store identity source. You can extract your entity attributes from a JSON Web Token (JWT) and parse it into Verified Permissions.

This example shows how you might call Verified Permissions from an OIDC IdP.¹

async function authorizeUsingJwtToken(jwtToken) { const payload = await verifier.verify(jwtToken); var principalEntity = { entityType: "PhotoFlash::User", // the application needs to fill in the relevant user type entityId: payload["sub"], // the application need to use the claim that represents the user-id }; var resourceEntity = { entityType: "PhotoFlash::Photo", //the application needs to fill in the relevant resource type entityId: "jane_photo_123.jpg", // the application needs to fill in the relevant resource id }; var action = { actionType: "PhotoFlash::Action", //the application needs to fill in the relevant action id actionId: "GetPhoto", //the application needs to fill in the relevant action type }; var entities = { entityList: [], }; entities.entityList.push(...getUserEntitiesFromToken(payload)); var policyStoreId = "PSEXAMPLEabcdefg111111"; // set your own policy store id const authResult = await client .isAuthorized({ policyStoreId: policyStoreId, principal: principalEntity, resource: resourceEntity, action: action, entities, }) .promise(); return authResult; } function getUserEntitiesFromToken(payload) { let attributes = {}; let claimsNotPassedInEntities = ['aud', 'sub', 'exp', 'jti', 'iss']; Object.entries(payload).forEach(([key, value]) => { if (claimsNotPassedInEntities.includes(key)) { return; } if (Array.isArray(value)) { var attibuteItem = []; value.forEach((item) => { attibuteItem.push({ string: item, }); }); attributes[key] = { set: attibuteItem, }; } else if (typeof value === 'string') { attributes[key] = { string: value, } } else if (typeof value === 'bigint' || typeof value ==='number') { attributes[key] = { long: value, } } else if (typeof value === 'bigint' || typeof value ==='number') { attributes[key] = { long: value, } } else if (typeof value === 'boolean') { attributes[key] = { boolean: value, } } }); let entityItem = { attributes: attributes, identifier: { entityType: "PhotoFlash::User", entityId: payload["sub"], // the application need to use the claim that represents the user-id } }; return [entityItem]; }

¹ This code example uses the aws-jwt-verify library for verifying JWTs signed by OIDC-compatible IdPs.