Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cognito): allow to set read and write attributes in Cognito UserPoolClient #7607

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
acc76d4
feat(cognito): allow set read write attributes in client
tom139 Apr 25, 2020
7ca180c
docs(cognito): add section for read/write attributes in clients
tom139 Apr 25, 2020
562cc4d
docs: improve read-write attributes section in README
tom139 May 3, 2020
f57d268
docs: improve read-write attribute section in user pool client
tom139 May 3, 2020
8fe041b
docs(cognito): add clarification about user pool client
tom139 May 3, 2020
401493f
Merge branch 'master' into tom139/cognito-userpool-client-set-readwri…
tom139 May 3, 2020
9315f41
docs(cognito): improve docstring in userpool
tom139 May 3, 2020
117eb53
feat(cognito): add AttributeSet and StandardAttributeMask
tom139 Dec 14, 2020
c95781b
fix: wrong attribute name for profile and picture
tom139 Dec 14, 2020
a9bbdeb
feat(cognito): add ability to set read and write attributes in client
tom139 Dec 14, 2020
36aa216
docs(cognito): for read and write attributes
tom139 Dec 14, 2020
3e45153
Merge branch 'cognito-userpool-client-set-read-write-attributes' into…
tom139 Dec 14, 2020
9b5e866
Merge branch 'master' into tom139/cognito-userpool-client-set-readwri…
tom139 Dec 14, 2020
41a2220
refactor: rename AttributeSet to ClientAttributes
tom139 Jan 12, 2021
56fe812
Merge branch 'master' into tom139/cognito-userpool-client-set-readwri…
tom139 Jan 12, 2021
9264458
refactor: ClientAttributes to more expressive API
tom139 Jan 12, 2021
76be44d
docs: fix comment on email_verified and phone_number_verified
tom139 Jan 12, 2021
9c986b3
docs: update default behaviour description for ClientAttributes
tom139 Jan 12, 2021
7765318
Merge branch 'master' into tom139/cognito-userpool-client-set-readwri…
tom139 Jan 13, 2021
6b6630c
fix: add emailVerified and phoneNumberVerified to StandardAttributes
tom139 Jan 14, 2021
2c2009d
refactor: remove ClientAttributes.empty() method
tom139 Jan 18, 2021
2af64b0
Merge branch 'master' into tom139/cognito-userpool-client-set-readwri…
tom139 Jan 18, 2021
b9e0a2c
docs: improve README on ClientAttributes
tom139 Jan 18, 2021
49f6eea
Merge branch 'master' into tom139/cognito-userpool-client-set-readwri…
mergify[bot] Jan 20, 2021
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
24 changes: 24 additions & 0 deletions packages/@aws-cdk/aws-cognito/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,30 @@ pool.addClient('app-client', {
});
```

Clients can (and should) be allowed to read and write relevant user attributes only. Usually every client can be allowed to read the `given_name`
attribute but not every client should be allowed to set the `email_verified` attribute.
The same criteria applies for both standard and custom attributes, more info is available at
[Attribute Permissions and Scopes](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html#user-pool-settings-attribute-permissions-and-scopes).
The default behaviour is to allow read and write permissions on all attributes. The following code shows how this can be configured for a client.

```ts
const pool = new cognito.UserPool(this, 'Pool');

const clientWriteAttributes = (new ClientAttributes())
.withStandardAttributes({name: true, email: true})
.withCustomAttributes(['favouritePizza']);

const clientReadAttributes = clientWriteAttributes
.withStandardAttributes({emailVerified: true})
.withCustomAttributes(['pointsEarned']);

pool.addClient('app-client', {
// ...
readAttributes: clientReadAttributes,
writeAttributes: clientWriteAttributes,
});
```

### Resource Servers

A resource server is a server for access-protected resources. It handles authenticated requests from an app that has an
Expand Down
2 changes: 2 additions & 0 deletions packages/@aws-cdk/aws-cognito/lib/private/attr-names.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ export const StandardAttributeNames = {
timezone: 'zoneinfo',
lastUpdateTime: 'updated_at',
website: 'website',
emailVerified: 'email_verified',
phoneNumberVerified: 'phone_number_verified',
};
200 changes: 200 additions & 0 deletions packages/@aws-cdk/aws-cognito/lib/user-pool-attr.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Token } from '@aws-cdk/core';
import { StandardAttributeNames } from './private/attr-names';

/**
* The set of standard attributes that can be marked as required or mutable.
Expand Down Expand Up @@ -107,6 +108,18 @@ export interface StandardAttributes {
* @default - see the defaults under `StandardAttribute`
*/
readonly website?: StandardAttribute;

/**
* Whether the email address has been verified.
* @default - see the defaults under `StandardAttribute`
*/
readonly emailVerified?: StandardAttribute;

/**
* Whether the phone number has been verified.
* @default - see the defaults under `StandardAttribute`
*/
readonly phoneNumberVerified?: StandardAttribute;
}

/**
Expand Down Expand Up @@ -341,3 +354,190 @@ export class DateTimeAttribute implements ICustomAttribute {
};
}
}

/**
* This interface contains all standard attributes recognized by Cognito
* from https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html
* including `email_verified` and `phone_number_verified`
*/
export interface StandardAttributesMask {
tom139 marked this conversation as resolved.
Show resolved Hide resolved
/**
* The user's postal address.
* @default false
*/
readonly address?: boolean;

/**
* The user's birthday, represented as an ISO 8601:2004 format.
* @default false
*/
readonly birthdate?: boolean;

/**
* The user's e-mail address, represented as an RFC 5322 [RFC5322] addr-spec.
* @default false
*/
readonly email?: boolean;

/**
* The surname or last name of the user.
* @default false
*/
readonly familyName?: boolean;

/**
* The user's gender.
* @default false
*/
readonly gender?: boolean;

/**
* The user's first name or give name.
* @default false
*/
readonly givenName?: boolean;

/**
* The user's locale, represented as a BCP47 [RFC5646] language tag.
* @default false
*/
readonly locale?: boolean;

/**
* The user's middle name.
* @default false
*/
readonly middleName?: boolean;

/**
* The user's full name in displayable form, including all name parts, titles and suffixes.
* @default false
*/
readonly fullname?: boolean;

/**
* The user's nickname or casual name.
* @default false
*/
readonly nickname?: boolean;

/**
* The user's telephone number.
* @default false
*/
readonly phoneNumber?: boolean;

/**
* The URL to the user's profile picture.
* @default false
*/
readonly profilePicture?: boolean;

/**
* The user's preffered username, different from the immutable user name.
* @default false
*/
readonly preferredUsername?: boolean;

/**
* The URL to the user's profile page.
* @default false
*/
readonly profilePage?: boolean;

/**
* The user's time zone.
* @default false
*/
readonly timezone?: boolean;

/**
* The time, the user's information was last updated.
* @default false
*/
readonly lastUpdateTime?: boolean;

/**
* The URL to the user's web page or blog.
* @default false
*/
readonly website?: boolean;

/**
* Whether the email address has been verified.
* @default false
*/
readonly emailVerified?: boolean;

/**
* Whether the phone number has been verified.
* @default false
*/
readonly phoneNumberVerified?: boolean;
}


/**
* A set of attributes, useful to set Read and Write attributes
*/
export class ClientAttributes {

/**
* The set of attributes
*/
private attributesSet: Set<string>;

/**
* Creates a ClientAttributes with the specified attributes
*
* @default - a ClientAttributes object without any attributes
*/
constructor() {
this.attributesSet = new Set<string>();
}

/**
* Creates a custom ClientAttributes with the specified attributes
* @param attributes a list of standard attributes to add to the set
*/
public withStandardAttributes(attributes: StandardAttributesMask): ClientAttributes {
let attributesSet = new Set(this.attributesSet);
// iterate through key-values in the `StandardAttributeNames` constant
// to get the value for all attributes
for (const attributeKey in StandardAttributeNames) {
if ((attributes as any)[attributeKey] === true) {
const attributeName = (StandardAttributeNames as any)[attributeKey];
attributesSet.add(attributeName);
}
}
let aux = new ClientAttributes();
aux.attributesSet = attributesSet;
return aux;
}

/**
* Creates a custom ClientAttributes with the specified attributes
* @param attributes a list of custom attributes to add to the set
*/
public withCustomAttributes(...attributes: string[]): ClientAttributes {
let attributesSet: Set<string> = new Set(this.attributesSet);
for (let attribute of attributes) {
// custom attributes MUST begin with `custom:`, so add the string if not present
if (!attribute.startsWith('custom:')) {
attribute = 'custom:' + attribute;
}
attributesSet.add(attribute);
}
let aux = new ClientAttributes();
aux.attributesSet = attributesSet;
return aux;
}

/**
* The list of attributes represented by this ClientAttributes
*/
public attributes(): string[] {
// sorting is unnecessary but it simplify testing
return Array.from(this.attributesSet).sort();
}
}
17 changes: 17 additions & 0 deletions packages/@aws-cdk/aws-cognito/lib/user-pool-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { IResource, Resource, Duration } from '@aws-cdk/core';
import { Construct } from 'constructs';
import { CfnUserPoolClient } from './cognito.generated';
import { IUserPool } from './user-pool';
import { ClientAttributes } from './user-pool-attr';
import { IUserPoolResourceServer, ResourceServerScope } from './user-pool-resource-server';

/**
Expand Down Expand Up @@ -272,6 +273,20 @@ export interface UserPoolClientOptions {
* @default Duration.minutes(60)
*/
readonly accessTokenValidity?: Duration;

/**
* The set of attributes this client will be able to read.
* @see https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html#user-pool-settings-attribute-permissions-and-scopes
* @default - all standard and custom attributes
*/
readonly readAttributes?: ClientAttributes;

/**
* The set of attributes this client will be able to write.
* @see https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html#user-pool-settings-attribute-permissions-and-scopes
* @default - all standard and custom attributes
*/
readonly writeAttributes?: ClientAttributes;
}

/**
Expand Down Expand Up @@ -358,6 +373,8 @@ export class UserPoolClient extends Resource implements IUserPoolClient {
allowedOAuthFlowsUserPoolClient: !props.disableOAuth,
preventUserExistenceErrors: this.configurePreventUserExistenceErrors(props.preventUserExistenceErrors),
supportedIdentityProviders: this.configureIdentityProviders(props),
readAttributes: props.readAttributes?.attributes(),
writeAttributes: props.writeAttributes?.attributes(),
});
this.configureTokenValidity(resource, props);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@
"PreventUserExistenceErrors": "ENABLED",
"SupportedIdentityProviders": [
"COGNITO"
],
"WriteAttributes": [
"address", "birthdate", "custom:attribute_one", "custom:attribute_two", "email",
"family_name", "gender", "given_name", "locale", "middle_name", "name", "nickname", "phone_number",
"picture", "preferred_username", "profile", "updated_at", "website", "zoneinfo"
]
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { App, Stack } from '@aws-cdk/core';
import { OAuthScope, UserPool } from '../lib';
import { OAuthScope, UserPool, ClientAttributes } from '../lib';

const app = new App();
const stack = new Stack(app, 'integ-user-pool-client-explicit-props');
Expand Down Expand Up @@ -30,4 +30,24 @@ userpool.addClient('myuserpoolclient', {
callbackUrls: ['https://redirect-here.myapp.com'],
},
preventUserExistenceErrors: true,
writeAttributes: (new ClientAttributes()).withStandardAttributes(
{
address: true,
birthdate: true,
email: true,
familyName: true,
fullname: true,
gender: true,
givenName: true,
lastUpdateTime: true,
locale: true,
middleName: true,
nickname: true,
phoneNumber: true,
preferredUsername: true,
profilePage: true,
profilePicture: true,
timezone: true,
website: true,
}).withCustomAttributes('attribute_one', 'attribute_two'),
});
Loading