Configure AWS Cognito User Pool Clients

The aws:cognito/userPoolClient:UserPoolClient resource, part of the Pulumi AWS provider, defines an application client for a Cognito user pool: its authentication flows, OAuth settings, and token behavior. This guide focuses on four capabilities: authentication flow selection, OAuth 2.0 configuration, analytics integration, and token rotation.

User pool clients belong to existing Cognito user pools and may reference Pinpoint applications or IAM roles for analytics. The examples are intentionally small. Combine them with your own user pools, identity providers, and monitoring infrastructure.

Create a minimal client for user pool access

Most integrations start by creating a client that applications use to authenticate users against a user pool.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const pool = new aws.cognito.UserPool("pool", {name: "pool"});
const client = new aws.cognito.UserPoolClient("client", {
    name: "client",
    userPoolId: pool.id,
});
import pulumi
import pulumi_aws as aws

pool = aws.cognito.UserPool("pool", name="pool")
client = aws.cognito.UserPoolClient("client",
    name="client",
    user_pool_id=pool.id)
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cognito"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		pool, err := cognito.NewUserPool(ctx, "pool", &cognito.UserPoolArgs{
			Name: pulumi.String("pool"),
		})
		if err != nil {
			return err
		}
		_, err = cognito.NewUserPoolClient(ctx, "client", &cognito.UserPoolClientArgs{
			Name:       pulumi.String("client"),
			UserPoolId: pool.ID(),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var pool = new Aws.Cognito.UserPool("pool", new()
    {
        Name = "pool",
    });

    var client = new Aws.Cognito.UserPoolClient("client", new()
    {
        Name = "client",
        UserPoolId = pool.Id,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.cognito.UserPool;
import com.pulumi.aws.cognito.UserPoolArgs;
import com.pulumi.aws.cognito.UserPoolClient;
import com.pulumi.aws.cognito.UserPoolClientArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var pool = new UserPool("pool", UserPoolArgs.builder()
            .name("pool")
            .build());

        var client = new UserPoolClient("client", UserPoolClientArgs.builder()
            .name("client")
            .userPoolId(pool.id())
            .build());

    }
}
resources:
  client:
    type: aws:cognito:UserPoolClient
    properties:
      name: client
      userPoolId: ${pool.id}
  pool:
    type: aws:cognito:UserPool
    properties:
      name: pool

The name property identifies the client, and userPoolId connects it to the user pool. Without additional configuration, the client uses Cognito’s default authentication flows and permissions.

Enable server-side authentication without SRP

Backend services that authenticate users through APIs often bypass the Secure Remote Password protocol in favor of direct username and password authentication.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const pool = new aws.cognito.UserPool("pool", {name: "pool"});
const client = new aws.cognito.UserPoolClient("client", {
    name: "client",
    userPoolId: pool.id,
    generateSecret: true,
    explicitAuthFlows: ["ADMIN_NO_SRP_AUTH"],
});
import pulumi
import pulumi_aws as aws

pool = aws.cognito.UserPool("pool", name="pool")
client = aws.cognito.UserPoolClient("client",
    name="client",
    user_pool_id=pool.id,
    generate_secret=True,
    explicit_auth_flows=["ADMIN_NO_SRP_AUTH"])
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cognito"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		pool, err := cognito.NewUserPool(ctx, "pool", &cognito.UserPoolArgs{
			Name: pulumi.String("pool"),
		})
		if err != nil {
			return err
		}
		_, err = cognito.NewUserPoolClient(ctx, "client", &cognito.UserPoolClientArgs{
			Name:           pulumi.String("client"),
			UserPoolId:     pool.ID(),
			GenerateSecret: pulumi.Bool(true),
			ExplicitAuthFlows: pulumi.StringArray{
				pulumi.String("ADMIN_NO_SRP_AUTH"),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var pool = new Aws.Cognito.UserPool("pool", new()
    {
        Name = "pool",
    });

    var client = new Aws.Cognito.UserPoolClient("client", new()
    {
        Name = "client",
        UserPoolId = pool.Id,
        GenerateSecret = true,
        ExplicitAuthFlows = new[]
        {
            "ADMIN_NO_SRP_AUTH",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.cognito.UserPool;
import com.pulumi.aws.cognito.UserPoolArgs;
import com.pulumi.aws.cognito.UserPoolClient;
import com.pulumi.aws.cognito.UserPoolClientArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var pool = new UserPool("pool", UserPoolArgs.builder()
            .name("pool")
            .build());

        var client = new UserPoolClient("client", UserPoolClientArgs.builder()
            .name("client")
            .userPoolId(pool.id())
            .generateSecret(true)
            .explicitAuthFlows("ADMIN_NO_SRP_AUTH")
            .build());

    }
}
resources:
  client:
    type: aws:cognito:UserPoolClient
    properties:
      name: client
      userPoolId: ${pool.id}
      generateSecret: true
      explicitAuthFlows:
        - ADMIN_NO_SRP_AUTH
  pool:
    type: aws:cognito:UserPool
    properties:
      name: pool

The explicitAuthFlows property controls which authentication methods the client can use. Setting it to ADMIN_NO_SRP_AUTH allows server-side authentication without the SRP protocol. The generateSecret property creates a client secret that backend services use to authenticate API requests.

Track user behavior with Pinpoint analytics

Applications that need to understand user engagement can route authentication events to Amazon Pinpoint for analysis.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const testUserPool = new aws.cognito.UserPool("test", {name: "pool"});
const testApp = new aws.pinpoint.App("test", {name: "pinpoint"});
const assumeRole = aws.iam.getPolicyDocument({
    statements: [{
        effect: "Allow",
        principals: [{
            type: "Service",
            identifiers: ["cognito-idp.amazonaws.com"],
        }],
        actions: ["sts:AssumeRole"],
    }],
});
const testRole = new aws.iam.Role("test", {
    name: "role",
    assumeRolePolicy: assumeRole.then(assumeRole => assumeRole.json),
});
const testUserPoolClient = new aws.cognito.UserPoolClient("test", {
    name: "pool_client",
    userPoolId: testUserPool.id,
    analyticsConfiguration: {
        applicationId: testApp.applicationId,
        externalId: "some_id",
        roleArn: testRole.arn,
        userDataShared: true,
    },
});
const current = aws.getCallerIdentity({});
const test = aws.iam.getPolicyDocumentOutput({
    statements: [{
        effect: "Allow",
        actions: [
            "mobiletargeting:UpdateEndpoint",
            "mobiletargeting:PutEvents",
        ],
        resources: [pulumi.all([current, testApp.applicationId]).apply(([current, applicationId]) => `arn:aws:mobiletargeting:*:${current.accountId}:apps/${applicationId}*`)],
    }],
});
const testRolePolicy = new aws.iam.RolePolicy("test", {
    name: "role_policy",
    role: testRole.id,
    policy: test.apply(test => test.json),
});
import pulumi
import pulumi_aws as aws

test_user_pool = aws.cognito.UserPool("test", name="pool")
test_app = aws.pinpoint.App("test", name="pinpoint")
assume_role = aws.iam.get_policy_document(statements=[{
    "effect": "Allow",
    "principals": [{
        "type": "Service",
        "identifiers": ["cognito-idp.amazonaws.com"],
    }],
    "actions": ["sts:AssumeRole"],
}])
test_role = aws.iam.Role("test",
    name="role",
    assume_role_policy=assume_role.json)
test_user_pool_client = aws.cognito.UserPoolClient("test",
    name="pool_client",
    user_pool_id=test_user_pool.id,
    analytics_configuration={
        "application_id": test_app.application_id,
        "external_id": "some_id",
        "role_arn": test_role.arn,
        "user_data_shared": True,
    })
current = aws.get_caller_identity()
test = aws.iam.get_policy_document_output(statements=[{
    "effect": "Allow",
    "actions": [
        "mobiletargeting:UpdateEndpoint",
        "mobiletargeting:PutEvents",
    ],
    "resources": [test_app.application_id.apply(lambda application_id: f"arn:aws:mobiletargeting:*:{current.account_id}:apps/{application_id}*")],
}])
test_role_policy = aws.iam.RolePolicy("test",
    name="role_policy",
    role=test_role.id,
    policy=test.json)
package main

import (
	"fmt"

	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cognito"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/iam"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/pinpoint"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		testUserPool, err := cognito.NewUserPool(ctx, "test", &cognito.UserPoolArgs{
			Name: pulumi.String("pool"),
		})
		if err != nil {
			return err
		}
		testApp, err := pinpoint.NewApp(ctx, "test", &pinpoint.AppArgs{
			Name: pulumi.String("pinpoint"),
		})
		if err != nil {
			return err
		}
		assumeRole, err := iam.GetPolicyDocument(ctx, &iam.GetPolicyDocumentArgs{
			Statements: []iam.GetPolicyDocumentStatement{
				{
					Effect: pulumi.StringRef("Allow"),
					Principals: []iam.GetPolicyDocumentStatementPrincipal{
						{
							Type: "Service",
							Identifiers: []string{
								"cognito-idp.amazonaws.com",
							},
						},
					},
					Actions: []string{
						"sts:AssumeRole",
					},
				},
			},
		}, nil)
		if err != nil {
			return err
		}
		testRole, err := iam.NewRole(ctx, "test", &iam.RoleArgs{
			Name:             pulumi.String("role"),
			AssumeRolePolicy: pulumi.String(assumeRole.Json),
		})
		if err != nil {
			return err
		}
		_, err = cognito.NewUserPoolClient(ctx, "test", &cognito.UserPoolClientArgs{
			Name:       pulumi.String("pool_client"),
			UserPoolId: testUserPool.ID(),
			AnalyticsConfiguration: &cognito.UserPoolClientAnalyticsConfigurationArgs{
				ApplicationId:  testApp.ApplicationId,
				ExternalId:     pulumi.String("some_id"),
				RoleArn:        testRole.Arn,
				UserDataShared: pulumi.Bool(true),
			},
		})
		if err != nil {
			return err
		}
		current, err := aws.GetCallerIdentity(ctx, &aws.GetCallerIdentityArgs{}, nil)
		if err != nil {
			return err
		}
		test := iam.GetPolicyDocumentOutput(ctx, iam.GetPolicyDocumentOutputArgs{
			Statements: iam.GetPolicyDocumentStatementArray{
				&iam.GetPolicyDocumentStatementArgs{
					Effect: pulumi.String("Allow"),
					Actions: pulumi.StringArray{
						pulumi.String("mobiletargeting:UpdateEndpoint"),
						pulumi.String("mobiletargeting:PutEvents"),
					},
					Resources: pulumi.StringArray{
						testApp.ApplicationId.ApplyT(func(applicationId string) (string, error) {
							return fmt.Sprintf("arn:aws:mobiletargeting:*:%v:apps/%v*", current.AccountId, applicationId), nil
						}).(pulumi.StringOutput),
					},
				},
			},
		}, nil)
		_, err = iam.NewRolePolicy(ctx, "test", &iam.RolePolicyArgs{
			Name: pulumi.String("role_policy"),
			Role: testRole.ID(),
			Policy: pulumi.String(test.ApplyT(func(test iam.GetPolicyDocumentResult) (*string, error) {
				return &test.Json, nil
			}).(pulumi.StringPtrOutput)),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var testUserPool = new Aws.Cognito.UserPool("test", new()
    {
        Name = "pool",
    });

    var testApp = new Aws.Pinpoint.App("test", new()
    {
        Name = "pinpoint",
    });

    var assumeRole = Aws.Iam.GetPolicyDocument.Invoke(new()
    {
        Statements = new[]
        {
            new Aws.Iam.Inputs.GetPolicyDocumentStatementInputArgs
            {
                Effect = "Allow",
                Principals = new[]
                {
                    new Aws.Iam.Inputs.GetPolicyDocumentStatementPrincipalInputArgs
                    {
                        Type = "Service",
                        Identifiers = new[]
                        {
                            "cognito-idp.amazonaws.com",
                        },
                    },
                },
                Actions = new[]
                {
                    "sts:AssumeRole",
                },
            },
        },
    });

    var testRole = new Aws.Iam.Role("test", new()
    {
        Name = "role",
        AssumeRolePolicy = assumeRole.Apply(getPolicyDocumentResult => getPolicyDocumentResult.Json),
    });

    var testUserPoolClient = new Aws.Cognito.UserPoolClient("test", new()
    {
        Name = "pool_client",
        UserPoolId = testUserPool.Id,
        AnalyticsConfiguration = new Aws.Cognito.Inputs.UserPoolClientAnalyticsConfigurationArgs
        {
            ApplicationId = testApp.ApplicationId,
            ExternalId = "some_id",
            RoleArn = testRole.Arn,
            UserDataShared = true,
        },
    });

    var current = Aws.GetCallerIdentity.Invoke();

    var test = Aws.Iam.GetPolicyDocument.Invoke(new()
    {
        Statements = new[]
        {
            new Aws.Iam.Inputs.GetPolicyDocumentStatementInputArgs
            {
                Effect = "Allow",
                Actions = new[]
                {
                    "mobiletargeting:UpdateEndpoint",
                    "mobiletargeting:PutEvents",
                },
                Resources = new[]
                {
                    $"arn:aws:mobiletargeting:*:{current.Apply(getCallerIdentityResult => getCallerIdentityResult.AccountId)}:apps/{testApp.ApplicationId}*",
                },
            },
        },
    });

    var testRolePolicy = new Aws.Iam.RolePolicy("test", new()
    {
        Name = "role_policy",
        Role = testRole.Id,
        Policy = test.Apply(getPolicyDocumentResult => getPolicyDocumentResult.Json),
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.cognito.UserPool;
import com.pulumi.aws.cognito.UserPoolArgs;
import com.pulumi.aws.pinpoint.App;
import com.pulumi.aws.pinpoint.AppArgs;
import com.pulumi.aws.iam.IamFunctions;
import com.pulumi.aws.iam.inputs.GetPolicyDocumentArgs;
import com.pulumi.aws.iam.Role;
import com.pulumi.aws.iam.RoleArgs;
import com.pulumi.aws.cognito.UserPoolClient;
import com.pulumi.aws.cognito.UserPoolClientArgs;
import com.pulumi.aws.cognito.inputs.UserPoolClientAnalyticsConfigurationArgs;
import com.pulumi.aws.AwsFunctions;
import com.pulumi.aws.inputs.GetCallerIdentityArgs;
import com.pulumi.aws.iam.RolePolicy;
import com.pulumi.aws.iam.RolePolicyArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var testUserPool = new UserPool("testUserPool", UserPoolArgs.builder()
            .name("pool")
            .build());

        var testApp = new App("testApp", AppArgs.builder()
            .name("pinpoint")
            .build());

        final var assumeRole = IamFunctions.getPolicyDocument(GetPolicyDocumentArgs.builder()
            .statements(GetPolicyDocumentStatementArgs.builder()
                .effect("Allow")
                .principals(GetPolicyDocumentStatementPrincipalArgs.builder()
                    .type("Service")
                    .identifiers("cognito-idp.amazonaws.com")
                    .build())
                .actions("sts:AssumeRole")
                .build())
            .build());

        var testRole = new Role("testRole", RoleArgs.builder()
            .name("role")
            .assumeRolePolicy(assumeRole.json())
            .build());

        var testUserPoolClient = new UserPoolClient("testUserPoolClient", UserPoolClientArgs.builder()
            .name("pool_client")
            .userPoolId(testUserPool.id())
            .analyticsConfiguration(UserPoolClientAnalyticsConfigurationArgs.builder()
                .applicationId(testApp.applicationId())
                .externalId("some_id")
                .roleArn(testRole.arn())
                .userDataShared(true)
                .build())
            .build());

        final var current = AwsFunctions.getCallerIdentity(GetCallerIdentityArgs.builder()
            .build());

        final var test = IamFunctions.getPolicyDocument(GetPolicyDocumentArgs.builder()
            .statements(GetPolicyDocumentStatementArgs.builder()
                .effect("Allow")
                .actions(                
                    "mobiletargeting:UpdateEndpoint",
                    "mobiletargeting:PutEvents")
                .resources(testApp.applicationId().applyValue(_applicationId -> String.format("arn:aws:mobiletargeting:*:%s:apps/%s*", current.accountId(),_applicationId)))
                .build())
            .build());

        var testRolePolicy = new RolePolicy("testRolePolicy", RolePolicyArgs.builder()
            .name("role_policy")
            .role(testRole.id())
            .policy(test.applyValue(_test -> _test.json()))
            .build());

    }
}
resources:
  testUserPoolClient:
    type: aws:cognito:UserPoolClient
    name: test
    properties:
      name: pool_client
      userPoolId: ${testUserPool.id}
      analyticsConfiguration:
        applicationId: ${testApp.applicationId}
        externalId: some_id
        roleArn: ${testRole.arn}
        userDataShared: true
  testUserPool:
    type: aws:cognito:UserPool
    name: test
    properties:
      name: pool
  testApp:
    type: aws:pinpoint:App
    name: test
    properties:
      name: pinpoint
  testRole:
    type: aws:iam:Role
    name: test
    properties:
      name: role
      assumeRolePolicy: ${assumeRole.json}
  testRolePolicy:
    type: aws:iam:RolePolicy
    name: test
    properties:
      name: role_policy
      role: ${testRole.id}
      policy: ${test.json}
variables:
  current:
    fn::invoke:
      function: aws:getCallerIdentity
      arguments: {}
  assumeRole:
    fn::invoke:
      function: aws:iam:getPolicyDocument
      arguments:
        statements:
          - effect: Allow
            principals:
              - type: Service
                identifiers:
                  - cognito-idp.amazonaws.com
            actions:
              - sts:AssumeRole
  test:
    fn::invoke:
      function: aws:iam:getPolicyDocument
      arguments:
        statements:
          - effect: Allow
            actions:
              - mobiletargeting:UpdateEndpoint
              - mobiletargeting:PutEvents
            resources:
              - arn:aws:mobiletargeting:*:${current.accountId}:apps/${testApp.applicationId}*

The analyticsConfiguration block connects the client to a Pinpoint application. The applicationId identifies the Pinpoint app, roleArn grants Cognito permission to send events, and userDataShared controls whether user attributes are included in analytics data. The IAM role requires permissions to call mobiletargeting:UpdateEndpoint and mobiletargeting:PutEvents.

Configure OAuth flows with callback URLs

Web and mobile applications using OAuth 2.0 need to specify where Cognito redirects users after authentication.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const pool = new aws.cognito.UserPool("pool", {name: "pool"});
const userpoolClient = new aws.cognito.UserPoolClient("userpool_client", {
    name: "client",
    userPoolId: pool.id,
    callbackUrls: ["https://example.com"],
    allowedOauthFlowsUserPoolClient: true,
    allowedOauthFlows: [
        "code",
        "implicit",
    ],
    allowedOauthScopes: [
        "email",
        "openid",
    ],
    supportedIdentityProviders: ["COGNITO"],
});
import pulumi
import pulumi_aws as aws

pool = aws.cognito.UserPool("pool", name="pool")
userpool_client = aws.cognito.UserPoolClient("userpool_client",
    name="client",
    user_pool_id=pool.id,
    callback_urls=["https://example.com"],
    allowed_oauth_flows_user_pool_client=True,
    allowed_oauth_flows=[
        "code",
        "implicit",
    ],
    allowed_oauth_scopes=[
        "email",
        "openid",
    ],
    supported_identity_providers=["COGNITO"])
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cognito"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		pool, err := cognito.NewUserPool(ctx, "pool", &cognito.UserPoolArgs{
			Name: pulumi.String("pool"),
		})
		if err != nil {
			return err
		}
		_, err = cognito.NewUserPoolClient(ctx, "userpool_client", &cognito.UserPoolClientArgs{
			Name:       pulumi.String("client"),
			UserPoolId: pool.ID(),
			CallbackUrls: pulumi.StringArray{
				pulumi.String("https://example.com"),
			},
			AllowedOauthFlowsUserPoolClient: pulumi.Bool(true),
			AllowedOauthFlows: pulumi.StringArray{
				pulumi.String("code"),
				pulumi.String("implicit"),
			},
			AllowedOauthScopes: pulumi.StringArray{
				pulumi.String("email"),
				pulumi.String("openid"),
			},
			SupportedIdentityProviders: pulumi.StringArray{
				pulumi.String("COGNITO"),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var pool = new Aws.Cognito.UserPool("pool", new()
    {
        Name = "pool",
    });

    var userpoolClient = new Aws.Cognito.UserPoolClient("userpool_client", new()
    {
        Name = "client",
        UserPoolId = pool.Id,
        CallbackUrls = new[]
        {
            "https://example.com",
        },
        AllowedOauthFlowsUserPoolClient = true,
        AllowedOauthFlows = new[]
        {
            "code",
            "implicit",
        },
        AllowedOauthScopes = new[]
        {
            "email",
            "openid",
        },
        SupportedIdentityProviders = new[]
        {
            "COGNITO",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.cognito.UserPool;
import com.pulumi.aws.cognito.UserPoolArgs;
import com.pulumi.aws.cognito.UserPoolClient;
import com.pulumi.aws.cognito.UserPoolClientArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var pool = new UserPool("pool", UserPoolArgs.builder()
            .name("pool")
            .build());

        var userpoolClient = new UserPoolClient("userpoolClient", UserPoolClientArgs.builder()
            .name("client")
            .userPoolId(pool.id())
            .callbackUrls("https://example.com")
            .allowedOauthFlowsUserPoolClient(true)
            .allowedOauthFlows(            
                "code",
                "implicit")
            .allowedOauthScopes(            
                "email",
                "openid")
            .supportedIdentityProviders("COGNITO")
            .build());

    }
}
resources:
  userpoolClient:
    type: aws:cognito:UserPoolClient
    name: userpool_client
    properties:
      name: client
      userPoolId: ${pool.id}
      callbackUrls:
        - https://example.com
      allowedOauthFlowsUserPoolClient: true
      allowedOauthFlows:
        - code
        - implicit
      allowedOauthScopes:
        - email
        - openid
      supportedIdentityProviders:
        - COGNITO
  pool:
    type: aws:cognito:UserPool
    properties:
      name: pool

Setting allowedOauthFlowsUserPoolClient to true enables OAuth 2.0 features. The allowedOauthFlows property specifies which OAuth flows the client supports (authorization code, implicit, or client credentials). The callbackUrls property lists valid redirect destinations after successful authentication. The allowedOauthScopes property controls which user attributes and permissions the client can request.

Rotate refresh tokens for enhanced security

Applications that need to detect token theft can enable automatic refresh token rotation.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const pool = new aws.cognito.UserPool("pool", {name: "pool"});
const userpoolClient = new aws.cognito.UserPoolClient("userpool_client", {
    name: "client",
    userPoolId: pool.id,
    explicitAuthFlows: ["ADMIN_NO_SRP_AUTH"],
    refreshTokenRotation: {
        feature: "ENABLED",
        retryGracePeriodSeconds: 10,
    },
});
import pulumi
import pulumi_aws as aws

pool = aws.cognito.UserPool("pool", name="pool")
userpool_client = aws.cognito.UserPoolClient("userpool_client",
    name="client",
    user_pool_id=pool.id,
    explicit_auth_flows=["ADMIN_NO_SRP_AUTH"],
    refresh_token_rotation={
        "feature": "ENABLED",
        "retry_grace_period_seconds": 10,
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cognito"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		pool, err := cognito.NewUserPool(ctx, "pool", &cognito.UserPoolArgs{
			Name: pulumi.String("pool"),
		})
		if err != nil {
			return err
		}
		_, err = cognito.NewUserPoolClient(ctx, "userpool_client", &cognito.UserPoolClientArgs{
			Name:       pulumi.String("client"),
			UserPoolId: pool.ID(),
			ExplicitAuthFlows: pulumi.StringArray{
				pulumi.String("ADMIN_NO_SRP_AUTH"),
			},
			RefreshTokenRotation: &cognito.UserPoolClientRefreshTokenRotationArgs{
				Feature:                 pulumi.String("ENABLED"),
				RetryGracePeriodSeconds: pulumi.Int(10),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var pool = new Aws.Cognito.UserPool("pool", new()
    {
        Name = "pool",
    });

    var userpoolClient = new Aws.Cognito.UserPoolClient("userpool_client", new()
    {
        Name = "client",
        UserPoolId = pool.Id,
        ExplicitAuthFlows = new[]
        {
            "ADMIN_NO_SRP_AUTH",
        },
        RefreshTokenRotation = new Aws.Cognito.Inputs.UserPoolClientRefreshTokenRotationArgs
        {
            Feature = "ENABLED",
            RetryGracePeriodSeconds = 10,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.cognito.UserPool;
import com.pulumi.aws.cognito.UserPoolArgs;
import com.pulumi.aws.cognito.UserPoolClient;
import com.pulumi.aws.cognito.UserPoolClientArgs;
import com.pulumi.aws.cognito.inputs.UserPoolClientRefreshTokenRotationArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var pool = new UserPool("pool", UserPoolArgs.builder()
            .name("pool")
            .build());

        var userpoolClient = new UserPoolClient("userpoolClient", UserPoolClientArgs.builder()
            .name("client")
            .userPoolId(pool.id())
            .explicitAuthFlows("ADMIN_NO_SRP_AUTH")
            .refreshTokenRotation(UserPoolClientRefreshTokenRotationArgs.builder()
                .feature("ENABLED")
                .retryGracePeriodSeconds(10)
                .build())
            .build());

    }
}
resources:
  userpoolClient:
    type: aws:cognito:UserPoolClient
    name: userpool_client
    properties:
      name: client
      userPoolId: ${pool.id}
      explicitAuthFlows:
        - ADMIN_NO_SRP_AUTH
      refreshTokenRotation:
        feature: ENABLED
        retryGracePeriodSeconds: 10
  pool:
    type: aws:cognito:UserPool
    properties:
      name: pool

The refreshTokenRotation block configures automatic token rotation. Setting feature to ENABLED issues new refresh tokens on each use and invalidates old ones. The retryGracePeriodSeconds property defines how long the old token remains valid during rotation, allowing for network retry scenarios.

Beyond these examples

These snippets focus on specific client-level features: authentication flow configuration, OAuth 2.0 integration with callbacks, and Pinpoint analytics and refresh token rotation. They’re intentionally minimal rather than full authentication systems.

The examples reference pre-existing infrastructure such as Cognito user pools, and Pinpoint applications and IAM roles for analytics examples. They focus on configuring the client rather than provisioning the surrounding authentication infrastructure.

To keep things focused, common client patterns are omitted, including:

  • Token validity configuration (accessTokenValidity, idTokenValidity, refreshTokenValidity)
  • Logout URLs and default redirect URIs
  • Attribute permissions (readAttributes, writeAttributes)
  • Token revocation and user context propagation

These omissions are intentional: the goal is to illustrate how each client feature is wired, not provide drop-in authentication modules. See the Cognito User Pool Client resource reference for all available configuration options.

Let's configure AWS Cognito User Pool Clients

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

OAuth & Identity Providers
Why can't I configure OAuth callback URLs or scopes?
You must set allowedOauthFlowsUserPoolClient to true before configuring callbackUrls, logoutUrls, allowedOauthScopes, or allowedOauthFlows.
How do I configure OAuth flows for my user pool client?
Set allowedOauthFlowsUserPoolClient to true, then configure callbackUrls, allowedOauthFlows (options: code, implicit, client_credentials), and allowedOauthScopes (options: email, openid, phone, profile, aws.cognito.signin.user.admin).
What identity providers can I configure for my user pool client?
Use supportedIdentityProviders with the providerName from aws.cognito.IdentityProvider resources, or use COGNITO for Cognito User Pool authentication.
When should I use ManagedUserPoolClient instead of UserPoolClient?
Use aws.cognito.ManagedUserPoolClient when another AWS service creates the client, such as when configuring an OpenSearch Domain to use Cognito authentication.
Authentication Flows & Security
How do I disable SRP authentication for my user pool client?
Set generateSecret to true and include ADMIN_NO_SRP_AUTH in explicitAuthFlows.
What authentication flows are available for user pool clients?
Available flows include ADMIN_NO_SRP_AUTH, CUSTOM_AUTH_FLOW_ONLY, USER_PASSWORD_AUTH, ALLOW_ADMIN_USER_PASSWORD_AUTH, ALLOW_CUSTOM_AUTH, ALLOW_USER_PASSWORD_AUTH, ALLOW_USER_SRP_AUTH, ALLOW_REFRESH_TOKEN_AUTH, and ALLOW_USER_AUTH.
What does the generateSecret property do?
When set to true, it generates an application secret for the user pool client, which is returned in the clientSecret output property.
Token Validity & Rotation
What are the token validity limits for user pool clients?
Access and ID tokens: 5 minutes to 1 day (default unit: hours). Refresh tokens: 60 minutes to 10 years (default unit: days). You can override units using tokenValidityUnits.
What's the valid range for auth session validity?
Between 3 and 15 minutes, with a default of 3 minutes. This controls how long the session token remains valid during an authentication flow.
How do I enable refresh token rotation?
Configure refreshTokenRotation with feature set to ENABLED and optionally set retryGracePeriodSeconds for retry handling.
Analytics & Integrations
How do I integrate Amazon Pinpoint analytics with my user pool client?
Configure analyticsConfiguration with applicationId, externalId, roleArn, and userDataShared. The IAM role requires mobiletargeting:UpdateEndpoint and mobiletargeting:PutEvents permissions.

Using a different cloud?

Explore security guides for other cloud providers: