Finishing AWS authentication flow - javascript

TLDR
I am looking for somewhere to send cognito JWT's from the backend to verify the user's status.
I currently have a react app, with a serverless apollo api, and dynamodb database, all running locally.
The react client uses aws-amplify to register, sign-in etc with
aws-cognito - returning access, id and refresh tokens.
Users can also sign in with facebook and google,
amplify.Auth.federatedSignIn which returns the cognito identity
credentials.
The tokens are then passed to the backend, where they are verified.
So far I cannot find where to send the tokens from the backend, to verify that the user is signed in to cognito.
I have scoured the docs but TBH that has left me more confused.
As far as I can understand, in production API Gateway, or AppSync can intecept the tokens between the front and backend, but since I have verified tokens at the backend currently is there an endpoint or SDK method I can hit with tokens/ access keys etc to check the users status?
Feel free to tell me if I'm going about this the wrong way.

If you need to verify that a token is valid and unexpired, with the JavaScript SDK use
const cognitoServiceProvider = new AWS.CognitoIdentityServiceProvider({apiVersion: '2016-04-18'});
await cognitoServiceProvider.getUser({
AccessToken: accessToken
}).promise();
This will throw an error if the token is not valid.
If you are using a different SDK, find the equivalent call.

Related

What is the authentication flow for SPA (React) + Amplify + Cognito + Backend? Is Amplify Cognito integration only for serverless apps?

What I have?
A client (React) and a backend (PHP) with a simple authentication flow: the client exchanges the id token with the back-end and keeps it in localStorage.
What I want to do?
I want to change the above approach to authentication with Cognito, to support Google Sign In and a sign-in against the user pool.
Important: I do not want to use Cognito Hosted UI or Amplify pre-built auth components. I already have UI for the authentication pages on my client, and I want to hook up the authentication with Cognito to that UI.
How am I doing it?
On the client, I am using Amplify library to implement the authentication against Cognito, as it provides all the functionality I need.
In the process of signing in on the client, the Amplify library gets all the tokens from Cognito in the following form:
{
AuthenticationResult: ""
AccessToken: "…"
ExpiresIn: 3600
IdToken: "..."
RefreshToken: "..."
TokenType: "Bearer"
ChallengeParameters: {}
}
Apart from that, the client receives a user object from the user pool with all the attributes.
Now this is a step I have questions about - what should be done next?
I realize that the client is already logged in to Cognito. However, the client still needs to call my own backend API for accessing the required resources.
I came up with the following options:
After signing in, the client could send the access token to the back-end, where the token can be verified, and, if valid, the back-end will respond with some kind of a flag that indicates to the client that it can call the API now.
After signing in, the client could send the access token to the back-end, back-end verifies the token, and issues a new token to access the back-end API only, which the client then stores and uses it against back-end endpoints.
I really got lost in Amplify documentation, and not even sure if I got everything right here. Is Amplify actually intended to be used only for serverless apps, or my case is also a valid scenario? If so, please let me know what would be the best practice here, and which approach should I take.
Thank you!

How to share auth0 authentication information between 2 different APIs?

Pretty much new on APIs and microservice world.
i am using auth0 for authentication.
I have a convert express API POST endpoint which will only work if the user is authenticated, however for frontend -> ( home page, login button, login from auth0 callback, redirection) I using different api Homepage express api.
After user logs in from homepage api, from his profile dashboard user tries to send a post request to convert API endpoint this doesn't work and throws an error check.state argument is missing.
How do I make sure if one API authenticates that authentication information should be shared with another API endpoint which needs authentication?
app.use(‘/authUrls’,requiresAuth(),authUrlsRouter) //convert api post request
This may be an assumption, but it appears that you are creating a regular web application with Node.js, and what you want is authentication per session, not per API endpoint. This quickstart from Auth0 walks through it nicely. It uses Passport.js and express-session to provide middleware.
As per the tutorial:
In a typical web application, the credentials used to authenticate a user are only transmitted during the login request. If authentication succeeds, a session is established and maintained via a cookie set in the user's browser. Each subsequent request does not contain credentials, but rather the unique cookie that identifies the session.
How it works: when the login api is called and completed successfully, the user's authentication is stored in that session. Whenever other API's are called that require an authenticated user, you can just include the middleware (in the quickstart it is called 'secured'): it will query that respective session's data and allow / disallow based on the user's authentication status.
For example, the convert endpoint:
router.post('/convert', secured(), this.convertfunction);
And a non-auth endpoint:
router.get('/other', this.otherfunction);
The full tutorial has much more information available. But this illustrates how middleware will solve your problem.

Should the Firebase Admin SDK or Client handle token refresh?

I am attempting to use firebase on my server for authentication and to provide an authentication experience for users that does not require pulling in firebase client side (for the moment).
A /login endpoint receives a username and password. Using firebase.auth().signInWithEmailAndPassword, I am then returning the JWT token to the user. They can then use that in subsequent requests to my RESTful API as the bearer token.
The problem I'm struggling with is when that token expires (after an hour). Part of my flow when a request comes in that requires auth validation is verifying the token with admin.auth().verifyIdToken, and rejecting the request if it's no longer valid.
Is it correct to:
(1) provide the client with the refresh token as well in the initial sign-in so they can handle the rejection and then request a new JWT token?
(2) perform some action on the admin SDK to refresh the token on the user's behalf
In typical oauth scenarios I've worked with before, the client has always been responsible for refreshing the token, however I can't for the life of me figure out how to retrieve a new JWT token from that refresh token using either the admin SDK or the firebase SDK
I don't know if the firebase SDK that usually resides client side is handling this token refresh behind the scenes whenever it is used with firebase.database, etc. If i'm not using that client SDK, but instead using the token as a bearer token, can I have the client pass the refresh token for exchange of a new JWT token from the backend?
It's likely staring me right in the face, I'm just not seeing it.
The Firebase Authentication SDKs keep the refresh token in the client-side code, together with the ID token, and then use the former to mint a new ID token when needed (about 5 minutes before the current one expires).

How to set AWS Cognito Tokens in localstorage

I am implementing SSO Authentication with AWS Cognito, Now I am able to successfully login a user and retrieve id_token, access_token, refresh_tokens..., and redirect the user to home page, So my happy flow is working fine,
Issue I am facing is when when ever a user is redirected to login page I check if any available session for the user with help of getCurrentUser(). Now when I login user with simple username and password I use
var cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
cognitoUser.authenticateUser(...)
Here I know after successful authentication token key are stored in my local-storage by aws-sdk. and these same keys are used by getCurrentUser method to fetch current user session.
Same I am not able to do in my Oauth apporach. I get the code in the query parameter and make a post call to fetch tokens. Now how to set the same token in my local storage to implement retrieveSession functionality.
The cognito sdk is specific to aws and is not a general purpose oauth sdk. Also note that this sdk (https://github.com/amazon-archives/amazon-cognito-identity-js) is now deprecated in favour of aws amplify js (https://github.com/aws-amplify/amplify-js).
If you want to use OAuth and openId connect approach, I would recommend to use oidc-client-js (https://github.com/IdentityModel/oidc-client-js).

How do I use SAML to connect AWS Cognito to a remote Shibboleth install?

Disclaimer: I am not well-versed in how Shibboleth or other authentication systems or AWS Cognito works, so for anyone that can help, please explain things in simple terms.
My company recently got a cloud version of our product running on AWS, and we now want to use AWS Cognito and SAML to connect to a remote Shibboleth system to authenticate users logging into our cloud system. The Shibboleth system is located remotely at a client's institution.
Thus far, I've done the following (and I don't know if any of this is correct):
In AWS, I created an IAM Identity Provider of the type SAML. I received a SAML.xml file from the client's IT department, which I connected to the IAM Identity Provider. This seems okay.
When I set up the IAM Identity Provider, AWS autocreated Cognito Auth and Unauth roles, for which I kept the default policies.
I set up a new Federated Identity for the client institution. I also linked the created IAM Identity Provider to this Federated Identity.
Upon creating the Federated Identity, I was taken to a Sample Code page in AWS, where I needed to pick the SDK to use for making a connection, etc. I also got an Identity Pool ID from AWS. I opted to use the JavaScript SDK, so I downloaded the JS SDK as well as another recommended library on GitHub called amazon-cognito-js-master.
I included the necessary JS scripts in a new HTML file and then put the JS code below in the file (with a valid Identity Pool ID).
When I ran the script, I seemed to get back a valid syncClient object from the console.log statement, but from there, I have no clue what to do.
Basically, what do I have to do at this point to make a request from our JS script to the remote Shibboleth system to authenticate a user?
Do I need to have the user's credentials (i.e., username and password) already available before I make an AWS.CognitoSyncManager request, or is there some level of initialization required before that? Is there other information/files I need from the client's institution / IT department to get this set up?
I know so little about this, I'm not even sure which questions to ask. Any help/guidance would be greatly appreciated.
Please note that I have already looked extensively at the AWS docs, but I couldn't make any sense out of what they were saying (I'm not an authentication expert). Thank you.
var IdentityPoolId = 'us-east-2:45679821-9064-45f8-12ac-456132abc789'; // Not a real ID.
// Initialize the Amazon Cognito credentials provider
AWS.config.region = 'us-east-2'; // Region
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: IdentityPoolId,
});
// Initialize the Cognito Sync client
AWS.config.credentials.get(function(){
var syncClient = new AWS.CognitoSyncManager();
console.log(syncClient);
syncClient.openOrCreateDataset('myDataset', function(err, dataset) {
dataset.put('myKey', 'myValue', function(err, record){
dataset.synchronize({
onSuccess: function(data, newRecords) {
// Your handler code here
}
});
});
});
});
Basically, what do I have to do at this point to make a request from our JS script to the remote Shibboleth system to authenticate a user?
Well, you can not. When using Federated Identities, you need to write your own code to display the login UI or redirect to the login page of third-party IdPs like Shibboleth or use their SDK. Ultimately, you need to write your own code, without any AWS SDK to get OpenID tokens or as in your case, SAML assertions. When you do get a token or SAML assertion, use this in the Login map while initializing CognitoIdentityCredentials. See the example here. There is a Logins field where you need to pass the token or assertion received from your IdP. Now, as long as the SAML assertion in the map is valid, you can get temporary AWS credentials & your AWS calls will succeed. This one line in the Login map is the only interaction between Cognito & your Idp like Shibboleth when using Federated Identities. You can not directly make a call from Cognito Federated Identities to Shibboleth with username & password to get a valid token or response.
But what if I want to build an app or a service which needs authentication using Shibboleth. Ideally, if the user is not signed in, I should be shown a UI which should redirect me to Shibboleth where I can enter my username & password. Afterwords, I should be redirected to my app/service with valid credentials.
This can be done too but using Cognito Userpool and not Federated Identities. Add your SAML provider to a Userpool (see this). After this, add the Userpool to your Identity Pool (Federated Identities). Now use the built-in UI of Userpool to login and get the Id token (and not SAML assertion, User --> Userpool built-in UI--> Shibboleth --> Username+Password to login --> Shibboleth sends SAML assertion to Cognito userpool--> Userpool issues a token). Use this Id token in the login map of your CognitoIdentityCredentials.

Categories

Resources