Hey everyone, Any example on how can i use JWT aut...
# sst
m
Hey everyone, Any example on how can i use JWT auth? I mean when and how a token would be generated and when and how it will be decoded etc?
c
Hi @Muhammad Ali haha thats a very broad question and I am not sure will actually answer what you want to know? If you're using AWS tools (i.e CDK, SST, Congito, Amplify, any of the SDKs) you don't really need to worry about the intricacies of JWT. I would also strongly suggest that you shouldn't worry about the intricacies of JWT as it is easy to mess up and make your app less secure. What specifically are you trying to achieve or are you actually interested in finding out more about JWT auth?
m
The reason i am asking about JWT is because I am unable to get
AWS_IAM
work for my use-case. see these 2 threads for details 1. https://serverless-stack.slack.com/archives/C01HQQVC8TH/p1634323975440100 2. https://serverless-stack.slack.com/archives/C01HQQVC8TH/p1634327676443000
c
Ok. I think you might be using the wrong features to achieve your goal, but instead of making assumptions, let me just clarify: 1. What are you authenticating? Is it users or services? 2. What are you authenticating for. Do you just want to have authenticated APIs or do you want the authenticated entites (users/services) to have access to other AWS services (s3bucket etc.)? 3. Where are you authenticating is it a frontend application?
m
@Chad (cysense) here is what i am trying to do. I am building a serverless backend (rest-apis, backed by lambda/dynamodb). These rest apis will eventually be used by end users via mobile app. end users will only use rest apis, they wouldn't have access/visibility to any other resource (s3 to upload pics but again via rest api). Consider my app as a clone of reddit or any other forum for that matter. the functionality would be exactly the same. So to answer your questions specifically 1. users 2. authenticated apis mostly. and in 1 use case, s3 bucket to upload pictures/download/view those pictures 3. frontend (mobile app)
The only reason i want "extra" claims in auth is to reduce dynamodb lookup for each api request. each api request has to figure out user's department (user would have visibility just to their department). There are 3 ways i can acheive this 1. Maintain a mapping of user - (active) department and on each api call find that department. This would be expensive since it will add 1 dynamo read operation on each call 2. Add department id for user in auth object, when user authenticate. And use this info in each request. this is the part which i am currently stuck at. i did add this info in
claimsToAddOrOverride
but i am unable to access/view it? 3. When user authenticate, return department id as encrypted value which can be used as request header in subsequent request. I haven't explored this option yet. But for this i need to understand what response is returned when a user authenticates and how to extract value out of it. i have been using
npx aws-api-gateway-cli-test
which abstracts everything, so would have to look into that.
m
@Muhammad Ali I have a sample repo I've been working on that demonstrates that: https://github.com/cliffom/sst-bff-demo Right now I'm just using the AWS CLI to generate my token but that would presumably by handled by your FE UI integration. The token is decoded as a JWT and accessible via the `RequestContext`: https://github.com/cliffom/sst-bff-demo/blob/main/src/handlers/api/users/main.go#L18
c
Ok great thats what I was thinking. Thanks for clarifying @Muhammad Ali. So the first point is just on the auth mechanism, you want to use CognitoUserPools, so this is
sst.ApiAuthorizationType.JWT
instead of
IAM
. You would use IAM if you want your users to access other AWS resources. @Michael Clifford's repo shows an example of setting this all up how you want. The second point on your 'extra' claims. The easiest solution would be to use your point 1 (maintain a mapping in the DB). This is what we have done and it gives us greater flexibility than doing it with cognito custom attributes. I can't imagine this would be much more expensive than what you are trying to do already. The trade off is one extra DDB read vs running a
preTokenGeneration
lambda trigger. I haven't done the math but I can imagine it is close. If you really don't want to a table for the mapping then you could look at using custom attributes instead. The biggest gotcha with custom attributes, is that you need to give your clients permission to read/write to custom attributes. You can have a look here on how to setup custom attributes https://bobbyhadz.com/blog/aws-cdk-cognito-user-pool-example#cognito-user-pool-client-in-aws-cdk---example. So in summary: 1. Use CognitoUserPools as the auth mechanism.
sst.ApiAuthorizationType.JWT
in SST 2. Use either a DB mapping or custom cognito attributes instead of a lambda trigger to handle granular user access in your app
m
It's worth calling out that custom attributes have to be created on the Cognito Pool before you can use them (meaning you can't create them arbitrarily)
c
Yeah great point!
s
@Chad (cysense) this is an interesting read my guy. I think everyone doing Auth, or Multi-tenant systems has to battle with custom attributes vs. dB lookups. I agree about the flexibility. It's disappointing the role based access control is so hard for cognito with serverless. I would just echo using dB lookups. Although you can add claims in the postConfirmation lambda, and avoid lookups. It's really only suitable for immutable data e.g. a user will always belong to a specific organisation in your multi-tenant system.
o
I’m using auth0 for this reason - its much easier to add custom claims to tokens, and the authorizer pretty much works the same as using cognito (I use cognito for authenticating API clients, and auth0 for humans).
m
@Chad (cysense) this is a very interstign take. i didn't realize of lambda execution cost. definitely changes a lot of things. Also what about option 3 i.e., returning encrypted values at the time of login which can then be aded to each request header?