amplify-backend
amplify-backend copied to clipboard
support multiple SAML providers
Environment information
System:
OS: macOS 14.5
CPU: (10) arm64 Apple M1 Pro
Memory: 136.41 MB / 32.00 GB
Shell: /opt/homebrew/bin/fish
Binaries:
Node: 20.16.0 - ~/.local/state/fnm_multishells/87752_1722097908671/bin/node
Yarn: undefined - undefined
npm: 10.8.1 - ~/.local/state/fnm_multishells/87752_1722097908671/bin/npm
pnpm: 9.6.0 - ~/.local/state/fnm_multishells/87752_1722097908671/bin/pnpm
NPM Packages:
@aws-amplify/auth-construct: Not Found
@aws-amplify/backend: 1.0.4
@aws-amplify/backend-auth: Not Found
@aws-amplify/backend-cli: 1.2.1
@aws-amplify/backend-data: Not Found
@aws-amplify/backend-deployer: 1.0.2
@aws-amplify/backend-function: Not Found
@aws-amplify/backend-output-schemas: 1.1.0
@aws-amplify/backend-output-storage: Not Found
@aws-amplify/backend-secret: 1.0.0
@aws-amplify/backend-storage: Not Found
@aws-amplify/cli-core: 1.1.1
@aws-amplify/client-config: 1.1.1
@aws-amplify/deployed-backend-client: 1.1.0
@aws-amplify/form-generator: 1.0.0
@aws-amplify/model-generator: 1.0.2
@aws-amplify/platform-core: 1.0.3
@aws-amplify/plugin-types: 1.1.0
@aws-amplify/sandbox: 1.1.1
@aws-amplify/schema-generator: 1.2.0
aws-amplify: 6.4.3
aws-cdk: 2.150.0
aws-cdk-lib: 2.150.0
typescript: 5.5.4
AWS environment variables:
AWS_PROFILE = josef
AWS_REGION = us-east-1
AWS_STS_REGIONAL_ENDPOINTS = regional
AWS_NODEJS_CONNECTION_REUSE_ENABLED = 1
AWS_SDK_LOAD_CONFIG = 1
No CDK environment variables
Description
In the event I have an "internal" and "external" SAML provider, I'd like to set both SAML providers on my user pool so I can programmatically redirect end users to sign in with one or the other based on some condition.
I’m facing a similar issue. We have configured multiple Azure Enterprise Apps for SSO login, with each app designated for a specific environment (dev, test, and prod). Is it possible to dynamically configure the metadataContent based on the git branch or environment variable? Because the metadataContent value is different in each app.
Hey @binli0114 you can store this as an environment variable or an SSM Parameter to reference in your auth definition
@josefaidt SSM Parameter won't work because they are resolved in runtime. Do you have example of using environment variable? Thanks
@josefaidt Thanks for the hint, I figured out how to do it.
Step1
Add preBuild commands in amplify.yml for the backend
backend:
phases:
preBuild:
commands:
- |
if [ "$AWS_BRANCH" = "dev" ]; then
echo "SAML_PROVIDER_APP_ID=123" >> .env
elif [ "$AWS_BRANCH" = "test" ]; then
echo "SAML_PROVIDER_APP_ID=456" >> .env
elif [ "$AWS_BRANCH" = "prod" ]; then
echo "SAML_PROVIDER_APP_ID=789" >> .env
fi
Step2
In the auth/resource.ts add following
import 'dotenv/config';
const SAML_PROVIDER_APP_ID = process.env.SAML_PROVIDER_APP_ID || '0000';
export const auth = defineAuth({
loginWith: {
email: true,
externalProviders: {
saml:{
name: "Azure-AD-SAML",
metadata:{
metadataContent:`https://login.microsoftonline.com/xxxxx/federationmetadata/2007-06/federationmetadata.xml?appid=${SAML_PROVIDER_APP_ID}`,
metadataType:"URL"
},
...
}
Another use case for multiple SAML providers is building multi-tenant enterprise SaaS on Amplify when different customers will be onboarded for SSO with their own identity providers. Currently, Amplify supports multiple OIDC providers but only one SAML. AWS Cognito doesn't have such a limitation, it's possible to configure multiple SAML providers for a single user pool.
CDK constructs can be used to add more providers:
// amplify/backend.ts
import * as cognito from "aws-cdk-lib/aws-cognito";
...
const backend = defineBackend({
auth,
data,
...
});
backend.auth.resources.userPool.registerIdentityProvider(
new cognito.UserPoolIdentityProviderSaml(
backend.auth.resources.userPool.stack,
"AnotherAuthProvider123",
{
userPool: backend.auth.resources.userPool,
name: "AnotherSamlProvider",
metadata: {
metadataContent: `...`,
metadataType: cognito.UserPoolIdentityProviderSamlMetadataType.FILE,
},
attributeMapping: {
email: cognito.ProviderAttribute.other("email"),
familyName: cognito.ProviderAttribute.other("family_name"),
givenName: cognito.ProviderAttribute.other("given_name"),
},
}
)
);
// amplify/auth/resource.ts
export const auth = defineAuth({
name: `ch-${process.env.AWS_BRANCH || "local"}`,
loginWith: {
email: true,
externalProviders: {
saml: {
name: "SomeSamlProvider",
metadata: {
metadataContent: `...`,
metadataType: "FILE",
},
attributeMapping: {
email: "email",
familyName: "family_name",
givenName: "given_name",
},
},
logoutUrls: [ ],
callbackUrls: [ ],
},
},
...
});
Results in two providers, SomeSamlProvider and AnotherSamlProvider. In our case, we're just going to define all of them the CDK way and remove from amplify/auth/resource.ts.
EDIT: This turned into more of a workaround than initially thought.
- There needs to be at least one external-provider configured in the auth-resource in order for Amplify to enable the feature.
- Each additional provider needs to be added to the App Client:
const cfnUserPoolClient = backend.auth.resources.userPoolClient.node .defaultChild as cognito.CfnUserPoolClient; cfnUserPoolClient.supportedIdentityProviders = [ ...(cfnUserPoolClient.supportedIdentityProviders || []), provider.name, ];