-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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
feat(cognito): allow to set read and write attributes in Cognito UserPoolClient #7607
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for submitting this PR.
I have some comments on this below.
Co-authored-by: Niranjan Jayakar <nija@amazon.com>
specifying how read/write attributes behave when new attributes get added to a user pool.
Co-authored-by: Niranjan Jayakar <nija@amazon.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please address existing comments.
any updates? |
Pull request has been modified.
Sorry for making you wait so long. 🙏🏽 I started from scratch with the approach suggested by @nija-at inspired by Naming of the methods may be misleading, but I couldn't think anything better and am open to suggestion. Usage is: const pool = new cognito.UserPool(this, 'Pool');
pool.addClient('app-client', {
// ...
readAttributes: AttributeSet.allStandard(['custom:my_attribute_1', 'custom:my_attribute_2']),
writeAttributes: AttributeSet.from({name: true, locale: true}, ['custom:my_attribute_1']),
}); |
Hey @tom139 - I'm going to be on vacation for the next couple of weeks for the holidays. I'll take a look at this once I'm back. |
Have a nice time and stay safe! :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for updating this PR. Some questions, comments and suggestions below.
/** | ||
* This interface contains all standard attributes recognized by Cognito | ||
* from https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html | ||
* including `preferred_email` and `preferred_phone_number` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Curious: why are preferred_email and preferred_number_number special?
They don't feature as standard attributes in the cognito docs. why are they considered as standard?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My bad. It is not preferred_email
and preferred_phone_number
to be special, it is email_verified
and phone_number_verified
.
These are special because are not directly mentioned in the list at the linked page but are present in the cited OpenID Connect Specification and are in fact present among the other standard attributes.
These two attributes are the missing ones from the cited StandardAttributes
interface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are in fact present among the other standard attributes
What do you mean by this? Were you able to confirm that they are actually supported by Cognito, but just missing in the documentation?
If that's the case, we should just add these to StandardAttributes
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have conducted some tests and I confirm Configuring User Pool is missing two attributes: email_verified
and phone_number_verified
.
Even though it states:
You get a set of default attributes, called "standard attributes," with all user pools. You can also add custom attributes to your user pool definition in the AWS Management Console. This topic describes those attributes in detail and gives you tips on how to set up your user pool.
and does not list them under standard attributes, the following call:
❯ aws cognito-idp create-user-pool --cli-input-json file://userpool.json
❯ cat userpool.json
{
"PoolName": "test",
"Policies": {
"PasswordPolicy": {
"MinimumLength": 10,
"RequireUppercase": true,
"RequireLowercase": true,
"RequireNumbers": true,
"RequireSymbols": true,
"TemporaryPasswordValidityDays": 10
}
},
"AutoVerifiedAttributes": [
"email"
],
"UsernameAttributes": [
"email"
],
"Schema": [
{
"Name": "email",
"AttributeDataType": "String",
"Mutable": true,
"Required": true,
"StringAttributeConstraints": {
"MinLength": "5",
"MaxLength": "2048"
}
},
{
"Name": "email_verified",
"AttributeDataType": "Boolean",
"Mutable": true,
"Required": true
}
],
"UsernameConfiguration": {
"CaseSensitive": true
}
}
works as expected, setting both email
and email_verified
as required and mutable attributes.
The UI Console has the same problem as it does not show (phone_number|email)_verified
in the attribute list:
I filed feedbacks for the documentation and the web ui pages.
That said, I will add those 2 attributes to the StandardAttributes
interface, so StandardAttributes
and StandardAttributesMask
will have the same properties.
Co-authored-by: Niranjan Jayakar <nija@amazon.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I addressed everything except setting up a test for detecting drifts between StandardAttributes
and StandardAttributesMask
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks closer. A few more comments. Hopefully the last round of review before merging 😊
/** | ||
* Creates an empty ClientAttributes | ||
*/ | ||
public static empty(): ClientAttributes { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we still need the ClientAttributes.empty()
method? Customers can just use the constructor new ClientAttributes()
, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Much nicer 👍🏽 I did the refactor.
The default behaviour is to allow read and write permissions on all attributes. | ||
|
||
```ts | ||
const pool = new cognito.UserPool(this, 'Pool'); | ||
// create a set of attributes that the client will be allowed to set | ||
const clientWriteAttributes = ClientAttributes.empty() | ||
.withStandardAttributes({name: true, email: true}) | ||
.withCustomAttributes(['favouritePizza']); | ||
// read attributes are usually more than the writable ones | ||
const clientReadAttributes = clientWriteAttributes | ||
.withStandardAttributes({emailVerified: true}) | ||
.withCustomAttributes(['pointsEarned']); | ||
pool.addClient('app-client', { | ||
// ... | ||
readAttributes: clientReadAttributes, | ||
writeAttributes: clientWriteAttributes, | ||
}); | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Slight tweaks to the documentation.
The default behaviour is to allow read and write permissions on all attributes. | |
```ts | |
const pool = new cognito.UserPool(this, 'Pool'); | |
// create a set of attributes that the client will be allowed to set | |
const clientWriteAttributes = ClientAttributes.empty() | |
.withStandardAttributes({name: true, email: true}) | |
.withCustomAttributes(['favouritePizza']); | |
// read attributes are usually more than the writable ones | |
const clientReadAttributes = clientWriteAttributes | |
.withStandardAttributes({emailVerified: true}) | |
.withCustomAttributes(['pointsEarned']); | |
pool.addClient('app-client', { | |
// ... | |
readAttributes: clientReadAttributes, | |
writeAttributes: clientWriteAttributes, | |
}); | |
``` | |
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 = ClientAttributes.empty() | |
.withStandardAttributes({name: true, email: true}) | |
.withCustomAttributes(['favouritePizza']); | |
const clientReadAttributes = clientWriteAttributes | |
.withStandardAttributes({emailVerified: true}) | |
.withCustomAttributes(['pointsEarned']); | |
pool.addClient('app-client', { | |
// ... | |
readAttributes: clientReadAttributes, | |
writeAttributes: clientWriteAttributes, | |
}); | |
``` |
and replace it with (new ClientAttribute())
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the time spent reviewing this!
Just a question more: am I supposed to hit "Resolve Conversation" when I think I addressed the issue, or should I let you do it?
/** | ||
* Creates an empty ClientAttributes | ||
*/ | ||
public static empty(): ClientAttributes { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Much nicer 👍🏽 I did the refactor.
Clicking 'resolve conversation' is fine once you've addressed it 😊 |
Thank you for contributing! Your pull request will be updated from master and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork). |
AWS CodeBuild CI Report
Powered by github-codebuild-logs, available on the AWS Serverless Application Repository |
Thank you for contributing! Your pull request will be updated from master and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork). |
…PoolClient (aws#7607) ### Commit Message feat(cognito): allow to set read and write attributes in Cognito UserPoolClient Add ability to specify which attributes each App Client can read or write. closes aws#7407 ### End Commit Message ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
Also, regarding #7607 (comment), I'd like to express interest in the additional static method you discussed, which would prepopulate all of the standard attributes. A common use case (I think) is to allow write access to everything except for a small number of custom attributes. To do this now, I'll have to manually list all of the standard attributes, and keep my manual list up to date if those ever change. Thanks again. |
To follow up on that last comment, here's the code I ended up writing: const standardWriteAttributes = new ClientAttributes().withStandardAttributes({
address: true,
birthdate: true,
email: true,
familyName: true,
gender: true,
givenName: true,
locale: true,
middleName: true,
fullname: true,
nickname: true,
phoneNumber: true,
profilePicture: true,
preferredUsername: true,
profilePage: true,
timezone: true,
lastUpdateTime: true,
website: true,
});
const standardReadAttributes = standardWriteAttributes.withStandardAttributes({
emailVerified: true,
phoneNumberVerified: true,
});
new UserPoolClient(this, "UserPoolClient", {
// Allow everything except for writing custom attributes.
readAttributes: standardReadAttributes.withCustomAttributes(...Object.keys(customAttributes)),
writeAttributes: standardWriteAttributes,
}); Would be great if those lists were exposed, perhaps via Note that they need to be separate lists, because |
Hi, thank you for your feedback! I totally agree with you. What do you think should be the best way to implement this? Having two all attributes methodsas you suggested: one for Writing and one for Reading attributes. Usage: const readAttr = (new ClientAttributes()).withAllStandardReadAttributes();
const writeAttr = (new ClientAttributes()).withAllStandardWriteAttributes(); Having only one method
Usage: const readAttr = (new ClientAttributes()).withAllStandardAttributes();
const writeAttr = (new ClientAttributes()).withAllStandardAttributes(); I think the latter would be is easier to use, what do you think? |
Sorry, I forgot to write about different design choices for withCustomAttributes() vs _withStandardAttributes()`. Having an interface for the standard attributes allow the user to easily find the available values through code completion; and in my opinion it is more valuable than consistency among |
I like the idea of having as few methods as possible, but in this case I think keeping them separate is best. Automatically removing the |
I'm with you about the value of code completion. Fortunately there's a way to have that without sacrificing consistency. For example: type StandardAttribute = "name" | "email";
function withStandardAttributes(...attributes: StandardAttribute[]); When you type |
Yeah, it is very cool! Unfortunately we must ensure that JSII can synthesise this information in other languages too… I don't think this will work outside of Typescript. 😔 @nija-at could you help us on that? |
Commit Message
feat(cognito): allow to set read and write attributes in Cognito UserPoolClient
Add ability to specify which attributes each App Client can read or write.
closes #7407
End Commit Message
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license