Configure AWS Cognito User Pool Clients

The aws:cognito/userPoolClient:UserPoolClient resource, part of the Pulumi AWS provider, defines application clients that connect to Cognito User Pools for authentication and authorization. This guide focuses on three capabilities: authentication flow configuration, OAuth 2.0 setup with callbacks and scopes, and token rotation security.

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

Create a minimal client for user pool access

Most integrations start with a basic client that connects applications to a user pool for 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,
});
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 client acts as the bridge between your application and Cognito. The name property identifies the client, and userPoolId links it to the user pool. Without additional configuration, the client uses Cognito’s default authentication flows and security settings.

Enable server-side authentication without SRP

Backend services that authenticate users through APIs often bypass the Secure Remote Password (SRP) protocol for direct username/password validation.

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 accepts. Setting it to ADMIN_NO_SRP_AUTH allows server-side applications to authenticate users directly without the SRP challenge-response flow. The generateSecret property creates a client secret that backend services use to prove their identity when calling Cognito APIs.

Configure OAuth flows with Cognito identity

Web and mobile applications using OAuth 2.0 need callback URLs, flow types, and permission scopes defined.

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

The allowedOauthFlowsUserPoolClient property must be true to enable OAuth features. The callbackUrls property lists where Cognito redirects users after authentication. The allowedOauthFlows property specifies whether to use authorization code flow (for server-side apps) or implicit flow (for single-page apps). The allowedOauthScopes property controls what user information the client can access. Setting supportedIdentityProviders to ["COGNITO"] uses Cognito’s built-in authentication without external providers.

Enable automatic refresh token rotation

Applications with long-lived sessions benefit from refresh token rotation, which automatically issues new tokens and invalidates old ones.

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 enables token rotation security. Setting feature to ENABLED activates automatic rotation. The retryGracePeriodSeconds property defines how long Cognito accepts the old refresh token after issuing a new one, allowing clients time to retry failed requests during the rotation window.

Beyond these examples

These snippets focus on specific client-level features: authentication flow configuration, OAuth 2.0 integration, and token rotation and validity. 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 integration. 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 tuning (accessTokenValidity, idTokenValidity, refreshTokenValidity)
  • Attribute permissions (readAttributes, writeAttributes)
  • User existence error handling (preventUserExistenceErrors)
  • Session validity configuration (authSessionValidity)

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

Resource Selection & Management
When should I use ManagedUserPoolClient instead of UserPoolClient?
Use aws.cognito.ManagedUserPoolClient when managing a client created by another AWS service, such as when configuring an OpenSearch Domain to use Cognito authentication. This prevents conflicts with service-managed clients.
OAuth 2.0 Configuration
Why can't I configure callback URLs or OAuth scopes?
You must set allowedOauthFlowsUserPoolClient to true before configuring callbackUrls, logoutUrls, allowedOauthScopes, or allowedOauthFlows. This enables OAuth 2.0 features for the client.
What OAuth flows are available?
Available flows include code, implicit, and client_credentials. Set allowedOauthFlowsUserPoolClient to true before configuring these flows.
What OAuth scopes are supported?
Supported scopes include phone, email, openid, profile, and aws.cognito.signin.user.admin.
How do I use Cognito as the identity provider?
Set supportedIdentityProviders to ["COGNITO"], enable OAuth with allowedOauthFlowsUserPoolClient: true, and configure callbackUrls, allowedOauthFlows (e.g., code, implicit), and allowedOauthScopes (e.g., email, openid).
Authentication Flows
What authentication flows are available?
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.
How do I disable SRP authentication?
Set generateSecret to true and include ADMIN_NO_SRP_AUTH in explicitAuthFlows.
Token Validity & Rotation
What are the token validity limits?

Token validity limits vary by type:

  • Access tokens: 5 minutes to 1 day (default unit: hours)
  • ID tokens: 5 minutes to 1 day (default unit: hours)
  • Refresh tokens: 60 minutes to 10 years (default unit: days)
  • Auth session tokens: 3 to 15 minutes (default: 3 minutes)

You can override default units using tokenValidityUnits.

How do I enable refresh token rotation?
Configure refreshTokenRotation with feature set to ENABLED and optionally specify retryGracePeriodSeconds (e.g., 10 seconds).
Integrations
How do I integrate Pinpoint analytics with my user pool client?
Configure analyticsConfiguration with applicationId, externalId, roleArn, and userDataShared. The IAM role needs mobiletargeting:UpdateEndpoint and mobiletargeting:PutEvents permissions for the Pinpoint app.
How do I specify identity providers?
Use supportedIdentityProviders with the providerName from aws.cognito.IdentityProvider resources or equivalent strings like COGNITO.

Using a different cloud?

Explore security guides for other cloud providers: