amplify-backend icon indicating copy to clipboard operation
amplify-backend copied to clipboard

support multiple SAML providers

Open josefaidt opened this issue 1 year ago • 6 comments

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.

josefaidt avatar Aug 01 '24 16:08 josefaidt

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.

binli0114 avatar Oct 23 '24 06:10 binli0114

Hey @binli0114 you can store this as an environment variable or an SSM Parameter to reference in your auth definition

josefaidt avatar Oct 23 '24 15:10 josefaidt

@josefaidt SSM Parameter won't work because they are resolved in runtime. Do you have example of using environment variable? Thanks

binli0114 avatar Oct 23 '24 22:10 binli0114

@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"
        },
...
}

binli0114 avatar Oct 24 '24 00:10 binli0114

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.

isabinin avatar Oct 24 '24 02:10 isabinin

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.

  1. There needs to be at least one external-provider configured in the auth-resource in order for Amplify to enable the feature.
  2. 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,
    ];
    

cBiscuitSurprise avatar Jan 21 '25 23:01 cBiscuitSurprise