Introduction
AWS Amplify simplifies adding authentication using Amazon Cognito User Pool.
However, there are situations where you need to use a different IdP — for example, due to organizational policy or when integrating AWS Amplify into an existing system.
In such cases, instead of using federated sign-in through Amazon Cognito User Pool, you can adopt the external ID provider approach via Amazon Cognito Identity Pool.
For instance, if your service already uses Auth0 by Okta, you are likely using the Auth0 SDK for login and Auth0's Universal Login for the sign-in screen.
You can keep this setup while also accessing backend resources added through AWS Amplify (such as AWS AppSync and Amazon DynamoDB) using authenticated credentials.
This article provides a guide on integrating Auth0 with Cognito Identity Pool Federation so that you can use Auth0 for authentication in AWS Amplify while accessing Amplify Data (AWS AppSync / Amazon DynamoDB) through the Amplify libraries.
The Official Documentation Is Not Enough
AWS Amplify has documentation for integrating Auth0:
However, the code examples alone are not sufficient to complete the implementation.
Required Information
Available in Official Documentation (with links or related info)
- Identity Pool Federation
- Code is provided on the page, but there is no explanation of file placement or project structure.
- Custom Token providers
- Code is provided on the page, but there is no explanation of file placement or project structure.
- Configuring OIDC authorization for AppSync (Data)
- Customize your auth rules / Amplify Docs
- Use OpenID Connect as an authorization provider / Amplify Docs
- The configuration options are documented, but there are no cross-links between pages — the information is fragmented.
- Auth0-side setup steps
- Integrate with Amazon Cognito / Auth0
Not in Official Documentation (no links or specific details)
- How to integrate with
@auth0/auth0-react - Defining an OIDC provider in Amplify Gen2 (CDK)
- class OpenIdConnectProvider (construct) / AWS CDK
- Environment variable management
- Environment variables can be used in
backend.ts, but there is no documentation about this.
- Environment variables can be used in
credentialsProvider and tokenProvider
-
credentialsProvider: Obtains temporary AWS credentials via Cognito Identity Pool (Identity Pool Federation) -
tokenProvider: Passes the ID token for AppSync OIDC authorization (Custom Token providers)
Both must be passed to Amplify.configure.
The official documentation explains them in separate sections, but for an Auth0 + AppSync setup, both are required.
Specifically, you need to configure both in Amplify.configure as follows:
// Example Amplify.configure setup. Details described later.
Amplify.configure(outputs, {
Auth: {
credentialsProvider: customCredentialsProvider,
tokenProvider: myTokenProvider,
},
});
Defining the OIDC Provider in backend.ts
The steps for registering an OIDC provider are documented on the Auth0 side: Integrate with Amazon Cognito.
How to use an OIDC provider is covered in Use OpenID Connect as an authorization provider.
However, since AWS Amplify Gen2 is integrated with AWS CDK, it is more natural to manage resources as custom resources within the Amplify project.
This aspect is not covered in the official documentation.
Auth0 Domain Format Differences
- Frontend (
@auth0/auth0-react):dev-xxx.us.auth0.com(withouthttps://) - Backend (IAM OIDC provider):
https://dev-xxx.us.auth0.com(withhttps://) - Cognito Identity Pool Logins key:
dev-xxx.us.auth0.com(withouthttps://)
You need to clearly understand where to use the "URL" format versus the "domain" format.
Implementation Steps
Step 1: Auth0 Configuration
- In Auth0 Dashboard → Applications → Applications, create an application (Single Page Application)
- Note the following:
- Domain (e.g.,
dev-xxx.us.auth0.com) - Client ID
- Domain (e.g.,
- Settings → Add
http://localhost:5173to Allowed Callback URLs - Settings → Add
http://localhost:5173to Allowed Logout URLs - Settings → Add
http://localhost:5173to Allowed Web Origins - Advanced Settings → OAuth → Verify that JSON Web Token (JWT) Signature Algorithm is RS256
Step 2: Amplify Project Setup
npm create amplify@latest
cd your-project
npm install @auth0/auth0-react @aws-sdk/client-cognito-identity
Step 3: Environment Variables
Create a .env file:
# Frontend (VITE_ prefix required for Vite)
VITE_AUTH0_DOMAIN=dev-xxx.us.auth0.com
VITE_AUTH0_CLIENT_ID=your-auth0-client-id
# Backend (used for OIDC provider creation, with https://)
AUTH0_DOMAIN=https://dev-xxx.us.auth0.com
AUTH0_CLIENT_ID=your-auth0-client-id
Note: VITE_AUTH0_DOMAIN does not include https://, while AUTH0_DOMAIN does.
Step 4: Amplify Backend Configuration
amplify/auth/resource.ts
When using Auth0 with Identity Pool Federation, you do not use externalProviders in defineAuth. Just define basic email authentication.
import { defineAuth } from "@aws-amplify/backend";
export const auth = defineAuth({
loginWith: {
email: true,
},
});
amplify/data/resource.ts
Set the default authorization mode for AppSync to OIDC.
Note: Auth0 tokens do not include auth_time, so you must set tokenExpiryFromAuthInSeconds to 0 to skip AppSync's validation. Otherwise, you will get a 401 error.
import { type ClientSchema, a, defineData } from "@aws-amplify/backend";
const schema = a.schema({
Todo: a
.model({
content: a.string(),
})
.authorization((allow) => [allow.owner("oidc").identityClaim("sub")]),
});
export type Schema = ClientSchema<typeof schema>;
export const data = defineData({
schema,
authorizationModes: {
defaultAuthorizationMode: "oidc",
oidcAuthorizationMode: {
oidcProviderName: "Auth0",
oidcIssuerUrl: "https://dev-xxx.us.auth0.com",
clientId: "your-auth0-client-id",
tokenExpiryFromAuthInSeconds: 0, // Auth0 tokens don't include auth_time, so set to 0 to skip AppSync validation.
tokenExpireFromIssueInSeconds: 3600,
},
},
});
amplify/backend.ts (Critical part not in official documentation)
Create an IAM OIDC provider at the CDK level and associate it with the Identity Pool. Use the dotenv package to load environment variables from .env.
import { defineBackend } from "@aws-amplify/backend";
import { auth } from "./auth/resource";
import { data } from "./data/resource";
import * as iam from "aws-cdk-lib/aws-iam";
import { config } from "dotenv";
config();
const backend = defineBackend({
auth,
data,
});
const auth0Domain = process.env.AUTH0_DOMAIN;
const auth0ClientId = process.env.AUTH0_CLIENT_ID;
if (!auth0Domain || !auth0ClientId) {
throw new Error(
"AUTH0_DOMAIN and AUTH0_CLIENT_ID must be set in environment variables",
);
}
// Create IAM OIDC provider
const oidcProvider = new iam.OpenIdConnectProvider(
backend.auth.resources.cfnResources.cfnIdentityPool.stack,
"Auth0OIDCProvider",
{
url: auth0Domain, // https://dev-xxx.us.auth0.com
clientIds: [auth0ClientId],
},
);
// Add Auth0 provider to Identity Pool
const identityPool = backend.auth.resources.cfnResources.cfnIdentityPool;
identityPool.openIdConnectProviderArns = [
...(identityPool.openIdConnectProviderArns || []),
oidcProvider.openIdConnectProviderArn,
];
Key point: backend.auth.resources.cfnResources.cfnIdentityPool gives you direct access to the L1 construct of the Identity Pool created by Amplify. This is the Amplify Gen2 escape hatch feature.
Step 5: Custom Credentials Provider (Frontend)
Create src/CustomCredentialsProvider.ts. This extends the official documentation sample and configures both tokenProvider and credentialsProvider together.
import { Amplify } from "aws-amplify";
import {
CredentialsAndIdentityIdProvider,
CredentialsAndIdentityId,
GetCredentialsOptions,
TokenProvider,
decodeJWT,
} from "aws-amplify/auth";
import { CognitoIdentity } from "@aws-sdk/client-cognito-identity";
import outputs from "../amplify_outputs.json";
const cognitoidentity = new CognitoIdentity({
region: outputs.auth.aws_region,
});
class CustomCredentialsProvider implements CredentialsAndIdentityIdProvider {
federatedLogin?: {
domain: string;
token: string;
};
loadFederatedLogin(login?: typeof this.federatedLogin) {
this.federatedLogin = login;
}
async getCredentialsAndIdentityId(
_getCredentialsOptions: GetCredentialsOptions,
): Promise<CredentialsAndIdentityId | undefined> {
try {
if (!this.federatedLogin) {
return undefined;
}
const getIdResult = await cognitoidentity.getId({
IdentityPoolId: outputs.auth.identity_pool_id,
Logins: { [this.federatedLogin.domain]: this.federatedLogin.token },
});
if (!getIdResult.IdentityId) {
throw new Error("Failed to get Identity ID");
}
const cognitoCredentialsResult =
await cognitoidentity.getCredentialsForIdentity({
IdentityId: getIdResult.IdentityId,
Logins: { [this.federatedLogin.domain]: this.federatedLogin.token },
});
if (!cognitoCredentialsResult.Credentials) {
throw new Error("Failed to get credentials");
}
return {
credentials: {
accessKeyId: cognitoCredentialsResult.Credentials.AccessKeyId!,
secretAccessKey: cognitoCredentialsResult.Credentials.SecretKey!,
sessionToken: cognitoCredentialsResult.Credentials.SessionToken,
expiration: cognitoCredentialsResult.Credentials.Expiration,
},
identityId: getIdResult.IdentityId,
};
} catch (e) {
console.log("Error getting credentials: ", e);
return undefined;
}
}
clearCredentialsAndIdentityId(): void {
this.federatedLogin = undefined;
}
}
// Custom token provider for AppSync OIDC authorization
// Note: The official docs explain credentialsProvider and tokenProvider in separate sections,
// but both are required for Auth0 + AppSync setups.
const myTokenProvider: TokenProvider = {
async getTokens() {
const tokenString = sessionStorage.getItem("auth0_id_token");
if (!tokenString) {
return null;
}
return {
accessToken: decodeJWT(tokenString),
idToken: decodeJWT(tokenString),
};
},
};
const customCredentialsProvider = new CustomCredentialsProvider();
// Configure both credentialsProvider and tokenProvider
Amplify.configure(outputs, {
Auth: {
credentialsProvider: customCredentialsProvider,
tokenProvider: myTokenProvider,
},
});
export default customCredentialsProvider;
Important points not in official documentation:
-
tokenProvideris required for AppSync OIDC authorization.credentialsProvideralone does not pass tokens to AppSync. - The ID token is stored in
sessionStorageand referenced bytokenProvider. - Both
accessTokenandidTokenare set to Auth0's ID token (because Auth0's SPA flow may not issue the access token in JWT format).
Step 6: Auth0 React SDK Integration
src/main.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import "./CustomCredentialsProvider"; // Import first to execute Amplify.configure
import { Auth0Provider } from "@auth0/auth0-react";
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<Auth0Provider
domain={import.meta.env.VITE_AUTH0_DOMAIN}
clientId={import.meta.env.VITE_AUTH0_CLIENT_ID}
authorizationParams={{
redirect_uri: window.location.origin,
}}
>
<App />
</Auth0Provider>
</React.StrictMode>
);
Note: Import "./CustomCredentialsProvider" before App so that Amplify.configure runs first.
src/App.tsx (Token handoff after Auth0 login)
import { useAuth0 } from "@auth0/auth0-react";
import { useEffect, useState } from "react";
import customCredentialsProvider from "./CustomCredentialsProvider";
function App() {
const { isAuthenticated, getIdTokenClaims } = useAuth0();
const [isTokenReady, setIsTokenReady] = useState(false);
useEffect(() => {
const loadCredentials = async () => {
if (isAuthenticated) {
const tokenClaims = await getIdTokenClaims();
if (tokenClaims?.__raw) {
// Pass Auth0 ID token to custom provider
customCredentialsProvider.loadFederatedLogin({
domain: import.meta.env.VITE_AUTH0_DOMAIN,
token: tokenClaims.__raw,
});
// Also store in sessionStorage for AppSync OIDC
sessionStorage.setItem("auth0_id_token", tokenClaims.__raw);
setIsTokenReady(true);
}
} else {
setIsTokenReady(false);
customCredentialsProvider.clearCredentialsAndIdentityId();
sessionStorage.removeItem("auth0_id_token");
}
};
loadCredentials();
}, [isAuthenticated, getIdTokenClaims]);
// Execute AppSync queries only after isTokenReady is true
// ...
}
Important points not in official documentation:
- Use
getIdTokenClaims().__rawto get the JWT string (the__rawproperty is not explicitly documented) - Pass the frontend domain (without
https://) toloadFederatedLogin'sdomain - Use the
isTokenReadystate to control the timing of AppSync query execution
Step 7: Executing AppSync Queries
import { generateClient } from "aws-amplify/api";
import type { Schema } from "../amplify/data/resource";
const client = generateClient<Schema>();
// Explicitly specify authMode: "oidc"
const { data } = await client.models.Todo.list({ authMode: "oidc" });
// Real-time subscriptions work the same way
client.models.Todo.observeQuery({ authMode: "oidc" }).subscribe({
next: (data) => console.log(data.items),
});
Step 8: Deployment
# Sandbox environment
npx ampx sandbox
# Production deployment (set environment variables in the console when using Amplify Hosting)
When using Amplify Hosting, set AUTH0_DOMAIN and AUTH0_CLIENT_ID as environment variables in the Amplify console. Variables with the VITE_ prefix are embedded at build time, so they also need to be set as build environment variables.
Troubleshooting
Invalid login token Error
- Verify that Auth0's JWT signature algorithm is RS256
- Check that the
Loginskey domain format is correct (with or withouthttps://) - Confirm that the Auth0 Client ID matches the IAM OIDC provider's Audience
No credentials / undefined credentials
- Make sure
loadFederatedLoginis called before executing AppSync queries - Use the
isTokenReadystate to control timing
Unauthorized Error in AppSync
- Verify that
tokenProvideris configured (credentialsProvideralone is not enough) - Check that
oidcIssuerUrlindata/resource.tsmatches the Auth0 domain - Explicitly specify
authMode: "oidc"in queries
NotAuthorizedException in Identity Pool
- Verify that the IAM OIDC provider URL and Audience are correct
- Confirm that Auth0 is included in the Identity Pool's authentication provider settings
Summary
The official documentation only explains the general approach of "creating a custom credentials provider and passing Auth0 tokens." To actually get it working, the following additional steps are required:
-
CDK-level OIDC provider definition (
backend.ts) -
tokenProviderimplementation (required for AppSync OIDC authorization) -
Auth0 React SDK integration code (token retrieval and handoff from the
useAuth0hook) -
Domain format differences for environment variables (with or without
https://) -
Token readiness timing management (
isTokenReadypattern)
Applying to Other OIDC Providers
This article uses Auth0 as an example, but this Identity Pool Federation pattern can be applied to any OIDC-compliant provider. For providers that issue ID tokens (JWT/RS256) — such as Okta, Google Workspace, Microsoft Entra ID (formerly Azure AD), or Keycloak — you only need to replace the following:
- Frontend authentication SDK (e.g.,
@okta/okta-react,@azure/msal-react, etc.) - The
domainpassed toloadFederatedLogin(the provider's issuer URL) - The IAM OIDC provider URL in
backend.ts -
oidcIssuerUrlandclientIdindata/resource.ts
The custom credentialsProvider / tokenProvider implementation pattern and CDK Identity Pool configuration can be reused as-is.
Reference Documentation
| # | Document | URL | Purpose |
|---|---|---|---|
| 1 | Amplify - Identity Pool Federation | https://docs.amplify.aws/react/build-a-backend/auth/advanced-workflows/#identity-pool-federation-3 | Custom credentials provider implementation pattern |
| 2 | Amplify - Federate with Auth0 | https://docs.amplify.aws/react/build-a-backend/auth/advanced-workflows/#federate-with-auth0 | Auth0 integration overview (limited information) |
| 3 | Amplify - Custom Token providers | https://docs.amplify.aws/react/build-a-backend/auth/advanced-workflows/#custom-token-providers | Token provider for AppSync OIDC authorization |
| 4 | Amplify - Customize your auth rules | https://docs.amplify.aws/react/build-a-backend/data/customize-authz/ | AppSync authorization rules |
| 5 | Amplify - Use OpenID Connect as an authorization provider | https://docs.amplify.aws/react/build-a-backend/data/customize-authz/using-oidc-authorization-provider/ | AppSync OIDC authorization configuration |
| 6 | Auth0 - Integrate with Amazon Cognito | https://auth0.com/docs/customize/integrations/aws/amazon-cognito | Auth0-side setup (IAM OIDC provider creation, etc.) |
| 7 | Auth0 React SDK | https://auth0.com/docs/quickstart/spa/react |
@auth0/auth0-react setup |
| 8 | AWS - Open ID Connect providers (Identity Pools) | https://docs.aws.amazon.com/cognito/latest/developerguide/open-id.html | OIDC integration with Cognito Identity Pool |
Top comments (0)