Ross Coundon
10/06/2021, 9:55 PMFrank
If you are using identity pool, it sounds very much like you are using IAM auth instead JWT? I’d double check on this, b/c with JWT, u shouldn’t need an identity pool. (Identity pool is responsible of assigning IAM permissions to auth and unauth users)and define asst.ApiAuthorizationType.JWT
as theHttpUserPoolAuthorizer
defaultAuthorizer
there were a tonne of policies dictating what an authorised user and unauthorised user can doIf u want the authed users to interact directly with ur resources (ie. upload directly to an S3 bucket without proxied through API, which is the recommended way) they’d need S3 policies. But most of the interactions are probably going to go through the API (ie. talk ing DB), so there shouldn’t be a tonne of policies for the identity pool.
is there a way to import an existing user pool and identity pool to form an Auth construct?Importing existing User Pool is not currently supported by high level CDK construct. Is this a blocker for u? I can dig into the low level Cfn constructs and put something together. Let me know!
Ross Coundon
10/06/2021, 10:34 PMdefaultAuthorizationType: ApiAuthorizationType.AWS_IAM,
is the fact that the Auth construct exists in the stack enough or am I missing some plumbing?
Regarding the importing, if we can't import an existing user pool, I think we'll need to write a little user migration tool to grab signed up users out of the existing one and add them to the new one, then ask them to reset their passwords which isn't the end of the world as there's only one customer org that it applies to right now.Chad (cysense)
10/07/2021, 3:24 AMIf I switch to IAM auth for the Api, how do I connect this up to the Auth construct?If your users are not using AWS services it sounds like you should be using userpool auth and not IAM auth? If that is the case then yeah you can import an existing user pool when creating the authorizer for the API.
const authorizer = new apigw.CognitoUserPoolsAuthorizer(this, 'Authorizer', {
cognitoUserPools: "AN OBJECT POINTING TO EXISTING USER POOL"
});
const v1 = apiGateway.api.root.addResource('v1')
const configs = v1.addResource('configs')
configs.addMethod('ANY', new apigw.LambdaIntegration(ConfigsFunction.lambda, { proxy: true }),
{
authorizer: authorizer,
authorizationType: apigw.AuthorizationType.COGNITO
}
)
Chad (cysense)
10/07/2021, 3:46 AMRoss Coundon
10/07/2021, 6:45 AMApiGatewayAuthorizer: {
Type: 'AWS::ApiGateway::Authorizer',
DependsOn: ['ApiGatewayRestApi'],
Properties: {
AuthorizerResultTtlInSeconds: 300,
IdentitySource: 'method.request.header.Authorization',
Name: 'cognito_authorizer',
RestApiId: {
Ref: 'ApiGatewayRestApi',
},
Type: 'COGNITO_USER_POOLS',
ProviderARNs: [
{
'Fn::GetAtt': ['CognitoUserPoolOmwUserPool', 'Arn'],
},
],
},
},
and this is references on an HTTP event like this:
SomeFunc: {
handler: '.build/main/handler/some.handler',
events: [
{
http: {
method: 'get',
path: '/somepath',
cors,
authorizer: {
type: 'COGNITO_USER_POOLS',
authorizerId: {
Ref: 'ApiGatewayAuthorizer',
},
},
},
},
],
},
So what you describe in terms of using a CognitoUserPoolsAuthorizer might actually be what I need. I'm trying to do this with the v2 HTTP API, whereas the legacy app was using the V1 ReST API - is your example above for v1?Ross Coundon
10/07/2021, 6:50 AMAmplify.configure({
Auth: {
mandatorySignIn: true,
region: amplifyConfig.cognito.REGION,
userPoolId: amplifyConfig.cognito.USER_POOL_ID,
identityPoolId: amplifyConfig.cognito.IDENTITY_POOL_ID,
userPoolWebClientId: amplifyConfig.cognito.APP_CLIENT_ID,
},
API: {
endpoints: [
{
name: 'someEndpoint',
endpoint: amplifyConfig.apiGateway.URL,
region: amplifyConfig.apiGateway.REGION,
custom_header: async () => {
const token = (await Auth.currentSession())
.getIdToken()
.getJwtToken();
return {
Authorization: `Bearer ${token}`,
};
},
},
],
},
});
Chad (cysense)
10/07/2021, 6:52 AMChad (cysense)
10/07/2021, 6:54 AMChad (cysense)
10/07/2021, 6:54 AMChad (cysense)
10/07/2021, 6:55 AMRoss Coundon
10/07/2021, 7:09 AMChad (cysense)
10/07/2021, 7:31 AMChad (cysense)
10/07/2021, 7:32 AMChad (cysense)
10/07/2021, 7:33 AMRoss Coundon
10/07/2021, 7:38 AMChad (cysense)
10/07/2021, 7:39 AMChad (cysense)
10/07/2021, 7:42 AMRoss Coundon
10/07/2021, 8:09 AMChad (cysense)
10/07/2021, 8:11 AMRoss Coundon
10/07/2021, 8:43 AMChad (cysense)
10/07/2021, 9:36 AMChad (cysense)
10/07/2021, 9:37 AMChad (cysense)
10/07/2021, 9:39 AMOPTIONS
to be unauthenticatedRoss Coundon
10/07/2021, 10:01 AMFrank
Frank
Auth
construct, your code would look something like this:
// IMPORT EXISTING USER POOL & USER POOL CLIENT
const userPool = cognito.UserPool.fromUserPoolArn(...);
const userPoolClient = cognito.UserPoolClient.fromUserPoolClientId(...);
// CREATE API AUTHORIZER
const authorizer = new apigv2Authorizers.HttpUserPoolAuthorizer({
userPool,
userPoolClient,
});
// CREATE API
new sst.Api(this, "Api", {
defaultAuthorizationType: ApiAuthorizationType.JWT,
defaultAuthorizer: authorizer,
defaultAuthorizationScopes: [..],
routes: {
"GET /notes": "src/list.main",
},
});
Frank
const userPool = // import
const userPoolClient = // import
const identityPool = // import
new sst.Auth(..) // pass in the 3 things above
// CREATE API
new sst.Api(this, "Api", {
defaultAuthorizationType: ApiAuthorizationType.IAM,
routes: {
"GET /notes": "src/list.main",
},
});
In the identity pool, you are granting IAM permissions to authed (and unauthed) users to be able to invoke the api. A benefit of using identity pool is that:
1. you can grant permissions to other AWS resources (ie. uploading to S3)
2. you can easily grant permissions to not-logged in users easilyFrank
Ross Coundon
10/07/2021, 4:35 PMconst api = new sst.Api(this, 'OmwPsoBeApi', {
defaultAuthorizationType: sst.ApiAuthorizationType.JWT,
defaultAuthorizer: new apigAuthorizers.HttpUserPoolAuthorizer({
userPool: cognitoAuth.cognitoUserPool,
userPoolClient: cognitoAuth.cognitoUserPoolClient,
}),
});
Does that look sensible?
Thanks for your detailed help!Frank
Frank
Auth
construct at the moment. I will keep you posted once we finalize on a solution for JWT.Ross Coundon
10/07/2021, 5:24 PMRoss Coundon
10/07/2021, 5:25 PMChad (cysense)
10/08/2021, 2:42 AMI'm in two minds about importing the existing user pool, there are 91 users in there that I could just import and have them reset their password, wouldn't be the end of the world.Just last 2c from me. If I was in your position, I would move away from letting Amplify manage your cognito pool. I am not sure how much you have used Amplify but its been a huge pain for us working on multi account team. Literally every second day something breaks in Amplify (tbh we might also just be doing it wrong). I've spent the last month slowly moving constructs from Amplify to CDK and everything has been much smoother. I am now only using Amplify for CD (considering moving to code pipelines) and using the amplify auth UI components. So just my 2c, I wouldn't suggest anyone building anything larger than a hobby project in Amplify yet
Ross Coundon
10/08/2021, 6:24 AMRoss Coundon
10/08/2021, 7:33 AMPreSignUp failed with error Missing "name" property.
But I can see via the console that name isn't a mandatory property so I'm thinking this is a side effect of some other issue.
I don't currently have an identity pool set up (although the legacy app did) so this could be an issue but the docs aren't clear on whether this is requiredChad (cysense)
10/08/2021, 7:36 AMChad (cysense)
10/08/2021, 7:38 AMRoss Coundon
10/08/2021, 7:46 AMChad (cysense)
10/08/2021, 8:08 AMthis.userPool = new cognito.UserPool(this, 'UserPool', {
selfSignUpEnabled: true,
removalPolicy: cdk.RemovalPolicy.DESTROY, // !! remove for production
signInAliases: {
email: true,
username: false
},
autoVerify: {
email: true
},
lambdaTriggers: {
preSignUp: this.PreSignupTrigger
}
});
this.userPool.addClient("Frontend")
Chad (cysense)
10/08/2021, 8:08 AMRoss Coundon
10/08/2021, 8:13 AMconst cognitoUserPool = new cognito.UserPool(this, 'cognitoUserPool', {
selfSignUpEnabled: true,
autoVerify: { email: true },
signInAliases: { email: true, username: false },
passwordPolicy: {
minLength: 8,
requireLowercase: true,
requireDigits: true,
requireSymbols: false,
requireUppercase: true,
},
accountRecovery: cognito.AccountRecovery.EMAIL_ONLY,
removalPolicy,
userVerification: {
emailBody: 'Thanks for signing up to the Appointment Assistant Portal! Your verification code is {####}',
emailSubject: 'Your Appointment Assistant Portal verification code',
emailStyle: cognito.VerificationEmailStyle.CODE,
},
lambdaTriggers: {
preSignUp: preSignUpFunction,
postConfirmation: postConfirmationFunction,
},
});
const standardCognitoAttributes: cognito.StandardAttributesMask = {
email: true,
emailVerified: true,
phoneNumberVerified: true,
};
const clientReadAttributes = new cognito.ClientAttributes().withStandardAttributes(standardCognitoAttributes);
const clientWriteAttributes = new cognito.ClientAttributes().withStandardAttributes({
...standardCognitoAttributes,
emailVerified: false,
phoneNumberVerified: false,
});
const cognitoUserPoolClient = new cognito.UserPoolClient(this, 'cognitoUserPoolClient', {
userPool: cognitoUserPool,
authFlows: {
userPassword: true,
},
supportedIdentityProviders: [cognito.UserPoolClientIdentityProvider.COGNITO],
readAttributes: clientReadAttributes,
writeAttributes: clientWriteAttributes,
});
Ross Coundon
10/08/2021, 8:14 AMsst start
for live debugging I just get
PreSignUp failed with error RequestId: 18989d45-4a90-4edd-99da-8b673c1da1d9 Error: Runtime exited with error: exit status 1
Ross Coundon
10/08/2021, 8:14 AMRoss Coundon
10/08/2021, 8:15 AMChad (cysense)
10/08/2021, 8:23 AMRoss Coundon
10/08/2021, 8:31 AMChad (cysense)
10/08/2021, 8:33 AMthis.userPool.addClient("Frontend")
does. But we have no need to read and write cognito attributes which its seems from your code you might need?Chad (cysense)
10/08/2021, 8:37 AMChad (cysense)
10/08/2021, 8:37 AMRoss Coundon
10/08/2021, 8:44 AMPostConfirmation failed with error Missing \"name\" property.
The peculiar thing is, these work when hooked in via the sls framework but with CDK.Ross Coundon
10/08/2021, 8:49 AMChad (cysense)
10/08/2021, 8:51 AMlambdaTriggers: {
preSignUp: preSignUpFunction,
postConfirmation: postConfirmationFunction,
},
This code should automatically setup the permissions for cognito to be able to invoke them so I don't think its a permissions issueChad (cysense)
10/08/2021, 8:52 AMChad (cysense)
10/08/2021, 8:53 AMChad (cysense)
10/08/2021, 8:54 AMRoss Coundon
10/08/2021, 9:02 AMsupportedIdentityProviders: [cognito.UserPoolClientIdentityProvider.COGNITO],
from the User Pool Client definition...Ross Coundon
10/08/2021, 9:05 AMChad (cysense)
10/08/2021, 9:42 AMauthFlows: {
userPassword: true,
userSrp: true
},
Ross Coundon
10/08/2021, 10:21 AM