Skip to content

AWS Single Sign-On

Introduction

AWS IAM Identity Center (formerly AWS Single Sign-On) is a cloud-based service that centralizes access management across multiple AWS accounts and business applications within an AWS Organization. It provides a unified portal for users to sign in once and access all their assigned accounts and applications, integrating with external identity providers via SAML 2.0 and OIDC. By decoupling human identity from IAM user credentials, it enforces least-privilege access patterns at organizational scale.


AWS SSO Architecture Overview


Permission Sets and Account Assignments

A Permission Set is a collection of IAM policies (AWS managed, customer managed, and inline) combined with session duration settings. It is instantiated as an IAM role in each assigned account.


SAML 2.0 Integration

When using an external IdP, AWS IAM Identity Center acts as a SAML 2.0 Service Provider. The IdP authenticates users and issues SAML assertions that Identity Center exchanges for AWS credentials.

SAML Authentication Sequence

SAML Metadata Configuration Snippet

xml
<?xml version="1.0" encoding="UTF-8"?>
<!--
  SAML 2.0 Service Provider Metadata for AWS IAM Identity Center.
  Upload this to your IdP to establish trust.
  Replace INSTANCE_ID with your Identity Center instance ID.
-->
<md:EntityDescriptor
    xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
    entityID="https://signin.aws.amazon.com/saml#INSTANCE_ID">

  <md:SPSSODescriptor
      AuthnRequestsSigned="false"
      WantAssertionsSigned="true"
      protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">

    <md:NameIDFormat>
      urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
    </md:NameIDFormat>

    <!-- Assertion Consumer Service — POST binding -->
    <md:AssertionConsumerService
        Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
        Location="https://d-XXXXXXXXXX.awsapps.com/start/saml/assertion-consumer"
        index="1"
        isDefault="true"/>

    <!-- Single Logout Service -->
    <md:SingleLogoutService
        Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
        Location="https://d-XXXXXXXXXX.awsapps.com/start/saml/logout"/>

  </md:SPSSODescriptor>
</md:EntityDescriptor>

OpenID Connect Integration

IAM Identity Center also supports OIDC-based identity providers. The OIDC protocol flow mirrors OAuth 2.0 Authorization Code, with Identity Center acting as the relying party.


Multi-Account Access Patterns


User and Group Management via Identity Store API

The Identity Store API (identitystore) manages users and groups within the built-in Identity Center directory.

Identity Store Operations in Java

java
import software.amazon.awssdk.services.identitystore.IdentitystoreClient;
import software.amazon.awssdk.services.identitystore.model.*;
import software.amazon.awssdk.regions.Region;

import java.util.List;

/**
 * Manages users and groups via the AWS Identity Store API (SDK v2).
 */
public class IdentityStoreManager {

    private final IdentitystoreClient client;
    private final String identityStoreId; // d-xxxxxxxxxx

    public IdentityStoreManager(String identityStoreId, Region region) {
        this.identityStoreId = identityStoreId;
        this.client = IdentitystoreClient.builder()
                .region(region)
                .build();
    }

    /** Creates a new user in the identity store. */
    public String createUser(String username, String givenName,
                             String familyName, String email) {
        CreateUserResponse response = client.createUser(CreateUserRequest.builder()
                .identityStoreId(identityStoreId)
                .userName(username)
                .name(Name.builder()
                        .givenName(givenName)
                        .familyName(familyName)
                        .build())
                .emails(Email.builder()
                        .value(email)
                        .type("work")
                        .primary(true)
                        .build())
                .build());
        System.out.println("Created user: " + response.userId());
        return response.userId();
    }

    /** Lists all users in the identity store. */
    public List<User> listUsers() {
        return client.listUsersPaginator(ListUsersRequest.builder()
                        .identityStoreId(identityStoreId)
                        .build())
                .users()
                .stream()
                .toList();
    }

    /** Creates a group and returns its ID. */
    public String createGroup(String displayName, String description) {
        CreateGroupResponse response = client.createGroup(CreateGroupRequest.builder()
                .identityStoreId(identityStoreId)
                .displayName(displayName)
                .description(description)
                .build());
        System.out.println("Created group: " + response.groupId());
        return response.groupId();
    }

    /** Adds a user to a group. */
    public String addUserToGroup(String userId, String groupId) {
        CreateGroupMembershipResponse response =
                client.createGroupMembership(CreateGroupMembershipRequest.builder()
                        .identityStoreId(identityStoreId)
                        .groupId(groupId)
                        .memberId(MemberId.builder().userId(userId).build())
                        .build());
        return response.membershipId();
    }

    public void close() {
        client.close();
    }
}

AWS SDK Implementation

AWSSSOManager — Permission Sets and Account Assignments

java
import software.amazon.awssdk.services.ssoadmin.SsoAdminClient;
import software.amazon.awssdk.services.ssoadmin.model.*;
import software.amazon.awssdk.regions.Region;

import java.util.List;

/**
 * Manages Permission Sets and account assignments via AWS SSO Admin API (SDK v2).
 */
public class AWSSSOManager {

    private final SsoAdminClient ssoAdmin;
    private final String instanceArn; // arn:aws:sso:::instance/ssoins-xxxxxxxxxx

    public AWSSSOManager(String instanceArn, Region region) {
        this.instanceArn = instanceArn;
        this.ssoAdmin = SsoAdminClient.builder().region(region).build();
    }

    /** Lists all roles (permission sets) available for a given account. */
    public List<String> listAccountRoles(String accountId, String accessToken) {
        // Uses the SSO portal token, not Admin credentials
        software.amazon.awssdk.services.sso.SsoClient ssoClient =
                software.amazon.awssdk.services.sso.SsoClient.builder()
                        .region(ssoAdmin.serviceClientConfiguration().region())
                        .build();

        return ssoClient.listAccountRolesPaginator(
                software.amazon.awssdk.services.sso.model.ListAccountRolesRequest.builder()
                        .accountId(accountId)
                        .accessToken(accessToken)
                        .build())
                .roleList()
                .stream()
                .map(r -> r.accountId() + "/" + r.roleName())
                .toList();
    }

    /** Creates a new Permission Set with a given name and session duration. */
    public String createPermissionSet(String name,
                                      String description,
                                      String sessionDuration) {
        CreatePermissionSetResponse response =
                ssoAdmin.createPermissionSet(CreatePermissionSetRequest.builder()
                        .instanceArn(instanceArn)
                        .name(name)
                        .description(description)
                        .sessionDuration(sessionDuration) // ISO 8601 e.g. PT4H
                        .build());

        String psArn = response.permissionSet().permissionSetArn();
        System.out.println("Created permission set: " + psArn);
        return psArn;
    }

    /** Attaches an AWS managed policy to a Permission Set. */
    public void attachManagedPolicy(String permissionSetArn,
                                    String managedPolicyArn) {
        ssoAdmin.attachManagedPolicyToPermissionSet(
                AttachManagedPolicyToPermissionSetRequest.builder()
                        .instanceArn(instanceArn)
                        .permissionSetArn(permissionSetArn)
                        .managedPolicyArn(managedPolicyArn)
                        .build());
        System.out.println("Attached policy " + managedPolicyArn);
    }

    /**
     * Assigns a user (principalType=USER) or group (principalType=GROUP)
     * to an AWS account with the given permission set.
     */
    public void assignUserToAccount(String permissionSetArn,
                                    String principalId,
                                    PrincipalType principalType,
                                    String targetAccountId) {
        CreateAccountAssignmentResponse response =
                ssoAdmin.createAccountAssignment(CreateAccountAssignmentRequest.builder()
                        .instanceArn(instanceArn)
                        .permissionSetArn(permissionSetArn)
                        .principalId(principalId)
                        .principalType(principalType)
                        .targetId(targetAccountId)
                        .targetType(TargetType.AWS_ACCOUNT)
                        .build());

        AccountAssignmentOperationStatus status =
                response.accountAssignmentCreationStatus();
        System.out.printf("Assignment status: %s (requestId=%s)%n",
                status.status(), status.requestId());
    }

    public void close() {
        ssoAdmin.close();
    }
}

Permission Set Management with Policy Attachment

java
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ssoadmin.model.PrincipalType;

/**
 * Example: provision a read-only Permission Set and assign a group to an account.
 */
public class PermissionSetProvisioner {

    private static final String INSTANCE_ARN =
            "arn:aws:sso:::instance/ssoins-xxxxxxxxxxxxxxxxx";
    private static final String IDENTITY_STORE_ID = "d-xxxxxxxxxx";
    private static final String TARGET_ACCOUNT_ID = "123456789012";

    public static void main(String[] args) {
        AWSSSOManager ssoManager = new AWSSSOManager(INSTANCE_ARN, Region.US_EAST_1);
        IdentityStoreManager idStore =
                new IdentityStoreManager(IDENTITY_STORE_ID, Region.US_EAST_1);

        // 1. Create permission set
        String psArn = ssoManager.createPermissionSet(
                "ReadOnlyEngineers",
                "Read-only access for engineering team members",
                "PT8H");

        // 2. Attach AWS managed policy
        ssoManager.attachManagedPolicy(psArn,
                "arn:aws:iam::aws:policy/ReadOnlyAccess");

        // 3. Create group in Identity Store
        String groupId = idStore.createGroup(
                "Engineering-ReadOnly",
                "Engineers with read-only access to production");

        // 4. Create a user and add to group
        String userId = idStore.createUser(
                "alice.smith",
                "Alice", "Smith",
                "alice.smith@example.com");
        idStore.addUserToGroup(userId, groupId);

        // 5. Assign group to target account with the permission set
        ssoManager.assignUserToAccount(
                psArn,
                groupId,
                PrincipalType.GROUP,
                TARGET_ACCOUNT_ID);

        System.out.println("Provisioning complete.");
        ssoManager.close();
        idStore.close();
    }
}

Multi-Account Access Using SsoClient

java
import software.amazon.awssdk.auth.credentials.AnonymousCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sso.SsoClient;
import software.amazon.awssdk.services.sso.model.*;
import software.amazon.awssdk.services.ssooidc.SsoOidcClient;
import software.amazon.awssdk.services.ssooidc.model.*;

/**
 * Demonstrates programmatic SSO login and multi-account credential retrieval.
 * This mirrors what `aws sso login` does under the hood.
 */
public class SSOCredentialRetriever {

    private static final String SSO_START_URL = "https://d-xxxxxxxxxx.awsapps.com/start";
    private static final Region SSO_REGION = Region.US_EAST_1;

    public static void retrieveCredentials(String accountId, String roleName) {
        SsoOidcClient oidcClient = SsoOidcClient.builder()
                .region(SSO_REGION)
                .credentialsProvider(AnonymousCredentialsProvider.create())
                .build();

        // Step 1: Register OIDC client
        RegisterClientResponse clientReg = oidcClient.registerClient(
                RegisterClientRequest.builder()
                        .clientName("my-cli-tool")
                        .clientType("public")
                        .build());

        // Step 2: Start device authorization
        StartDeviceAuthorizationResponse deviceAuth = oidcClient.startDeviceAuthorization(
                StartDeviceAuthorizationRequest.builder()
                        .clientId(clientReg.clientId())
                        .clientSecret(clientReg.clientSecret())
                        .startUrl(SSO_START_URL)
                        .build());

        System.out.println("Open this URL to authenticate: "
                + deviceAuth.verificationUriComplete());

        // Step 3: Poll for token (user completes browser login)
        CreateTokenResponse tokenResponse = oidcClient.createToken(
                CreateTokenRequest.builder()
                        .clientId(clientReg.clientId())
                        .clientSecret(clientReg.clientSecret())
                        .grantType("urn:ietf:params:oauth:grant-type:device_code")
                        .deviceCode(deviceAuth.deviceCode())
                        .build());

        String accessToken = tokenResponse.accessToken();

        // Step 4: Use access token to get role credentials
        SsoClient ssoClient = SsoClient.builder()
                .region(SSO_REGION)
                .credentialsProvider(AnonymousCredentialsProvider.create())
                .build();

        GetRoleCredentialsResponse creds = ssoClient.getRoleCredentials(
                GetRoleCredentialsRequest.builder()
                        .accountId(accountId)
                        .roleName(roleName)
                        .accessToken(accessToken)
                        .build());

        RoleCredentials roleCredentials = creds.roleCredentials();
        System.out.printf("AccessKeyId: %s%n", roleCredentials.accessKeyId());
        System.out.printf("Expiration:  %s%n", roleCredentials.expiration());

        oidcClient.close();
        ssoClient.close();
    }
}

Best Practices

  1. Centralized identity, no IAM users — Eliminate long-lived IAM user access keys. All human access should flow through IAM Identity Center using federated, short-lived credentials (default 1–8 hour session).

  2. Federate with your corporate IdP — Connect Identity Center to your Active Directory or external IdP (Okta, Azure AD, Ping) via SCIM provisioning. This ensures JIT deprovisioning: when a user is disabled in the IdP, their SSO access is revoked immediately.

  3. Least-privilege permission sets — Create role-specific permission sets (e.g., DeveloperAccess, ReadOnlyAccess, DatabaseAdmin) rather than AdministratorAccess. Attach only the managed or customer-managed policies needed.

  4. Use groups, not individual assignments — Assign permission sets to groups, not individual users. Group membership is managed in the IdP and synced via SCIM, making access changes auditable and scalable.

  5. Periodic access reviews — Schedule quarterly access reviews using AWS Access Analyzer and the Identity Store API. Automate reports of all account assignments and flag unused permission sets.

  6. MFA enforcement at the IdP — Require MFA at the identity provider level. For users with the built-in identity store, enforce MFA via IAM Identity Center's MFA settings (TOTP / FIDO2).

  7. Enable CloudTrail for SSO events — All SSO API calls and console sign-in events are logged in CloudTrail under the sso.amazonaws.com event source. Create CloudWatch Alarms on ConsoleLogin failures.

  8. Session duration aligned with risk — Set shorter session durations (1–2 hours) for privileged permission sets (e.g., production admin). Set longer durations (8 hours) for read-only developer access to balance security and productivity.