My web application uses React in the frontend and Flask in the backend.
I would like to add AWS Cognito for user management. As per the documentation, this requires nothing more than wrapping an export
statement in the frontend, i.e. changing a line
export default App;
in an App.js
file (defining the main application React component) to
export default withAuthenticator(App, true);
plus adding the required import
statements.
This trivial change gets me all the user-focussed functionality required by any reasonable web application used in production, e.g.
- user signup
- email verification
- login / logout
- forgotten password
- etc. pp.
including
- themable UI
- email sending services
- federated logins (e.g. Google, Facebook, etc.)
The problem: I don't see how to integrate this with the backend.
The approach: (as I think it should be)
- an existing user logs into the web application using the UI elements provided by Cognito and displayed by the frontend
- upon successful login, Cognito makes user data available by means of
currentAuthenticatedUser()
; among others, a JSON Web Token (JWT) - whenever the frontend sends requests to the backend, it passes that JWT as a request header
- the backend uses that JWT to determine if the user has properly logged in, which user the request is made for and if that user has sufficient permissions
I have implemented the usual Flask /login
or /logout
request handlers and a login_required
decorator and also passing the JWT and decoding claims works fine; however, I don't see where the frontend should notify the backend about a user logging in.
I tried doing this with a Cognito Post Authentication Lambda Trigger, but this has a number of massive drawbacks:
For starters, the AWS lambda function triggered by the user login must be able to access the backend, so it must be publicly available. For security reasons, I would actually prefer the backend to be visible / accessible to / by the frontend only. Also, during development, I'm running both frontend and backend in Docker containers on my MacBook; this approach forces me to run at least the backend in the cloud.
Biggest problem though is that it doesn't work: From the backend perspective, requests from the frontend and Cognito take place in separate sessions, so even after the user logs in using Cognito (via the frontend) and Cognito notifies the backend about this login (via a lambda trigger), any subsequent requests from the frontend to the backend - from the backend perspective - are not associated with the (authenticated) session between backend and Cognito and are therefore unauthenticated.
Well, apart from the massive drawbacks and the fact that it doesn't work, the lambda trigger approach also feels total overkill.
Update: I don't know why I didn't see this earlier, but there is a section in the Amplify docs that exactly explains how to customize withAuthenticator
. However, I don't want to override render()
as in the example, but signIn()
(or so I believe). I have successfully implemented the code to send a request to http://mybackend.mydomain.com/login
with appropriate headers to log into the backend, but I can't seem to figure out where / how to call that code.
I have copied the code from the Amplify docs linked above, looked at the SignIn
JSX source code and the ("rendered" / "built" ?) version in my local source code folder at node_modules/aws-amplify-react/dist/Auth/SignIn.js
and tried this:
class MySignIn extends SignIn {
async signIn() {
console.log('MySignIn.signIn() start');
super.signIn()
.then(data => console.log(data))
.catch(err => console.log(err));
console.log('MySignIn.signIn() end');
}
}
but all this gets me is an empty page and no messages in the log whatsoever.
New question: How do I implement MySignIn
so that it does all the SignIn
work, but after successful login, also does the additional work I need ?
Thank you very much for your consideration! :-)