Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-cognito/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,7 @@ The following third-party identity providers are currently supported in the CDK
- [Google Login](https://developers.google.com/identity/sign-in/web/sign-in)
- [Sign In With Apple](https://developer.apple.com/sign-in-with-apple/get-started/)
- [OpenID Connect](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-oidc-idp.html)
- [SAML](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-saml-idp.html)

The following code configures a user pool to federate with the third party provider, 'Login with Amazon'. The identity
provider needs to be configured with a set of credentials that the Cognito backend can use to federate with the
Expand Down
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-cognito/lib/user-pool-idps/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './amazon';
export * from './facebook';
export * from './google';
export * from './oidc';
export * from './saml';
132 changes: 132 additions & 0 deletions packages/@aws-cdk/aws-cognito/lib/user-pool-idps/saml.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { Names, Token } from '@aws-cdk/core';
import { Construct } from 'constructs';
import { CfnUserPoolIdentityProvider } from '../cognito.generated';
import { UserPoolIdentityProviderProps } from './base';
import { UserPoolIdentityProviderBase } from './private/user-pool-idp-base';

/**
* Properties to initialize UserPoolIdentityProviderSaml.
*/
export interface UserPoolIdentityProviderSamlProps extends UserPoolIdentityProviderProps {
/**
* The name of the provider. Must be between 3 and 32 characters.
*
* @default - the unique ID of the construct
*/
readonly name?: string;

/**
* Identifiers
*
* Identifiers can be used to redirect users to the correct IdP in multitenant apps.
*
* @default - no identifiers used
*/
readonly identifiers?: string[]

/**
* The SAML metadata.
*/
readonly metadata: UserPoolIdentityProviderSamlMetadata;

/**
* Whether to enable the "Sign-out flow" feature.
*
* @default - false
*/
readonly idpSignout?: boolean;
}

/**
* Metadata types that can be used for a SAML user pool identity provider.
*/
export enum UserPoolIdentityProviderSamlMetadataType {
/** Metadata provided via a URL. */
URL = 'url',

/** Metadata provided via the contents of a file. */
FILE = 'file',
}

/**
* Metadata for a SAML user pool identity provider.
*/
export class UserPoolIdentityProviderSamlMetadata {

/**
* Specify SAML metadata via a URL.
*/
public static url(url: string): UserPoolIdentityProviderSamlMetadata {
return new UserPoolIdentityProviderSamlMetadata(url, UserPoolIdentityProviderSamlMetadataType.URL);
}

/**
* Specify SAML metadata via the contents of a file.
*/
public static file(fileContent: string): UserPoolIdentityProviderSamlMetadata {
return new UserPoolIdentityProviderSamlMetadata(fileContent, UserPoolIdentityProviderSamlMetadataType.FILE);
}

/**
* Construct the metadata for a SAML identity provider.
*
* @param metadataContent A URL hosting SAML metadata, or the content of a file containing SAML metadata.
* @param metadataType The type of metadata, either a URL or file content.
*/
private constructor(public readonly metadataContent: string, public readonly metadataType: UserPoolIdentityProviderSamlMetadataType) {
}
}

/**
* Represents a identity provider that integrates with SAML.
* @resource AWS::Cognito::UserPoolIdentityProvider
*/
export class UserPoolIdentityProviderSaml extends UserPoolIdentityProviderBase {
public readonly providerName: string;

constructor(scope: Construct, id: string, props: UserPoolIdentityProviderSamlProps) {
super(scope, id, props);

this.validateName(props.name);

const { metadataType, metadataContent } = props.metadata;

const resource = new CfnUserPoolIdentityProvider(this, 'Resource', {
userPoolId: props.userPool.userPoolId,
providerName: this.getProviderName(props.name),
providerType: 'SAML',
providerDetails: {
IDPSignout: props.idpSignout ?? false,
MetadataURL: metadataType === UserPoolIdentityProviderSamlMetadataType.URL ? metadataContent : undefined,
MetadataFile: metadataType === UserPoolIdentityProviderSamlMetadataType.FILE ? metadataContent : undefined,
},
idpIdentifiers: props.identifiers,
attributeMapping: super.configureAttributeMapping(),
});

this.providerName = super.getResourceNameAttribute(resource.ref);
}

private getProviderName(name?: string): string {
if (name) {
this.validateName(name);
return name;
}

const uniqueName = Names.uniqueResourceName(this, {
maxLength: 32,
});

if (uniqueName.length < 3) {
return `${uniqueName}saml`;
}

return uniqueName;
}

private validateName(name?: string) {
if (name && !Token.isUnresolved(name) && (name.length < 3 || name.length > 32)) {
throw new Error(`Expected provider name to be between 3 and 32 characters, received ${name} (${name.length} characters)`);
}
}
}
4 changes: 3 additions & 1 deletion packages/@aws-cdk/aws-cognito/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
"@aws-cdk/assertions": "0.0.0",
"@aws-cdk/cdk-build-tools": "0.0.0",
"@aws-cdk/integ-runner": "0.0.0",
"@aws-cdk/integ-tests": "0.0.0",
"@aws-cdk/cfn2ts": "0.0.0",
"@aws-cdk/pkglint": "0.0.0",
"@types/jest": "^27.5.2",
Expand Down Expand Up @@ -126,7 +127,8 @@
"props-physical-name:@aws-cdk/aws-cognito.UserPoolIdentityProviderAmazonProps",
"props-physical-name:@aws-cdk/aws-cognito.UserPoolIdentityProviderGoogleProps",
"props-physical-name:@aws-cdk/aws-cognito.UserPoolIdentityProviderAppleProps",
"props-physical-name:@aws-cdk/aws-cognito.UserPoolIdentityProviderOidcProps"
"props-physical-name:@aws-cdk/aws-cognito.UserPoolIdentityProviderOidcProps",
"props-physical-name:@aws-cdk/aws-cognito.UserPoolIdentityProviderSamlProps"
]
},
"stability": "stable",
Expand Down
40 changes: 40 additions & 0 deletions packages/@aws-cdk/aws-cognito/test/integ.user-pool-idp.saml.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { App, CfnOutput, RemovalPolicy, Stack } from '@aws-cdk/core';
import { IntegTest } from '@aws-cdk/integ-tests';
import { Construct } from 'constructs';
import { UserPool, UserPoolIdentityProviderSaml, UserPoolIdentityProviderSamlMetadata } from '../lib';

class TestStack extends Stack {
constructor(scope: Construct, id: string) {
super(scope, id);
const userpool = new UserPool(this, 'pool', {
removalPolicy: RemovalPolicy.DESTROY,
});

new UserPoolIdentityProviderSaml(this, 'cdk', {
userPool: userpool,
name: 'cdk',
metadata: UserPoolIdentityProviderSamlMetadata.url('https://fujifish.github.io/samling/public/metadata.xml'),
});

const client = userpool.addClient('client');

const domain = userpool.addDomain('domain', {
cognitoDomain: {
domainPrefix: 'cdk-test-pool',
},
});

new CfnOutput(this, 'SignInLink', {
value: domain.signInUrl(client, {
redirectUri: 'https://example.com',
}),
});
}
}

const app = new App();
const testCase = new TestStack(app, 'integ-user-pool-identity-provider-saml-stack');

new IntegTest(app, 'integ-user-pool-identity-provider-saml-test', {
testCases: [testCase],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"version":"21.0.0"}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"version": "21.0.0",
"files": {
"6f6f07786415216f13b738979cec5ad81dbab3283fae83b99324965935cc1d60": {
"source": {
"path": "integ-user-pool-identity-provider-saml-stack.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "6f6f07786415216f13b738979cec5ad81dbab3283fae83b99324965935cc1d60.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
}
},
"dockerImages": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
{
"Resources": {
"pool056F3F7E": {
"Type": "AWS::Cognito::UserPool",
"Properties": {
"AccountRecoverySetting": {
"RecoveryMechanisms": [
{
"Name": "verified_phone_number",
"Priority": 1
},
{
"Name": "verified_email",
"Priority": 2
}
]
},
"AdminCreateUserConfig": {
"AllowAdminCreateUserOnly": true
},
"EmailVerificationMessage": "The verification code to your new account is {####}",
"EmailVerificationSubject": "Verify your new account",
"SmsVerificationMessage": "The verification code to your new account is {####}",
"VerificationMessageTemplate": {
"DefaultEmailOption": "CONFIRM_WITH_CODE",
"EmailMessage": "The verification code to your new account is {####}",
"EmailSubject": "Verify your new account",
"SmsMessage": "The verification code to your new account is {####}"
}
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
},
"poolclient2623294C": {
"Type": "AWS::Cognito::UserPoolClient",
"Properties": {
"UserPoolId": {
"Ref": "pool056F3F7E"
},
"AllowedOAuthFlows": [
"implicit",
"code"
],
"AllowedOAuthFlowsUserPoolClient": true,
"AllowedOAuthScopes": [
"profile",
"phone",
"email",
"openid",
"aws.cognito.signin.user.admin"
],
"CallbackURLs": [
"https://example.com"
],
"SupportedIdentityProviders": [
{
"Ref": "cdk52888317"
},
"COGNITO"
]
}
},
"pooldomain430FA744": {
"Type": "AWS::Cognito::UserPoolDomain",
"Properties": {
"Domain": "cdk-test-pool",
"UserPoolId": {
"Ref": "pool056F3F7E"
}
}
},
"cdk52888317": {
"Type": "AWS::Cognito::UserPoolIdentityProvider",
"Properties": {
"ProviderName": "cdk",
"ProviderType": "SAML",
"UserPoolId": {
"Ref": "pool056F3F7E"
},
"ProviderDetails": {
"IDPSignout": false,
"MetadataURL": "https://fujifish.github.io/samling/public/metadata.xml"
}
}
}
},
"Outputs": {
"SignInLink": {
"Value": {
"Fn::Join": [
"",
[
"https://",
{
"Ref": "pooldomain430FA744"
},
".auth.",
{
"Ref": "AWS::Region"
},
".amazoncognito.com/login?client_id=",
{
"Ref": "poolclient2623294C"
},
"&response_type=code&redirect_uri=https://example.com"
]
]
}
}
},
"Parameters": {
"BootstrapVersion": {
"Type": "AWS::SSM::Parameter::Value<String>",
"Default": "/cdk-bootstrap/hnb659fds/version",
"Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"
}
},
"Rules": {
"CheckBootstrapVersion": {
"Assertions": [
{
"Assert": {
"Fn::Not": [
{
"Fn::Contains": [
[
"1",
"2",
"3",
"4",
"5"
],
{
"Ref": "BootstrapVersion"
}
]
}
]
},
"AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
}
]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": "21.0.0",
"testCases": {
"integ-user-pool-identity-provider-saml-test/DefaultTest": {
"stacks": [
"integ-user-pool-identity-provider-saml-stack"
],
"assertionStack": "integ-user-pool-identity-provider-saml-test/DefaultTest/DeployAssert",
"assertionStackName": "integuserpoolidentityprovidersamltestDefaultTestDeployAssert97F09C26"
}
}
}
Loading