How to use Cognito as OAuth Flow without using Hosted UI? - amazon-web-services

I saw some similar questions here before but I couldn`t understand none of them. How can I get an code from OAuth 2 authentication flow without using hosted UI in Cognito ?
I saw that hosted calls a javascript function but I don`t understand how this works and return the code.
Is there any way to call an API directly passing user credentials and has this code generated ?

The documentation is pretty bad for this - but I got it to work using a customProvider as a property
Auth.federatedSignIn(
{customProvider: "YOUR_CUSTOM_PROVIDER"})
According to:
https://github.com/aws-amplify/amplify-js/blob/a047ce73/packages/auth/src/types/Auth.ts#L74
The whole function based on the AWS tutorial would then be:
Auth.federatedSignIn(
{customProvider: "YOUR_CUSTOM_PROVIDER"}
).then(cred => {
// If success, you will get the AWS credentials
console.log(cred);
this.setState({
isAuthorized:true
})
this.login()
}).then(user => {
// If success, the user object you passed in Auth.federatedSignIn
console.log(user);
}).catch(e => {
console.log(e)
});
}

Related

AWS Amplify Auth.updateUserAttributes() clear localStorage/sessionStorage

I am writing a web-based app that uses AWS Cognito as the authentication service. I use 'aws-amplify' to implement the client-app.
I am using Auth.updateUserAttributes() to update a custom attribute of users on Cognito. However, I found that the call of this function would clear all the Cognito-related items, including idToken, refreshToken, and accessToken stored in localStorage. As a result, the web app behaves like sign-out.
Here is the code about Auth's configuration
Amplify.configure({
Auth: {
userPoolId: process.env.REACT_APP_AWS_COGNITO_USER_POOL_ID,
region: process.env.REACT_APP_AWS_COGNITO_REGION,
userPoolWebClientId: process.env.REACT_APP_AWS_COGNITO_APP_CLIENT_ID,
storage: window.localStorage,
authenticationFlowType: 'CUSTOM_AUTH',
},
});
and the code I wrote to update the user's attribute. (I followed the example code from the amplify docs https://docs.amplify.aws/lib/auth/manageusers/q/platform/js/#managing-user-attributes)
let user = await Auth.currentAuthenticatedUser();
console.log(JSON.stringify(localStorage)); // it outputs the localstorage with idToken,
// refreshToken, accessToken and other items
// start with 'CognitoIdentityServiceProvider'
const result = await Auth.updateUserAttributes(user, {
'custom:attributes_1': '123456789',
});
console.log(result); // OUTPUT: SUCCESS
console.log(JSON.stringify(localStorage)); // Only 'amplify-signin-with-hostedUI'.
// idToken, refreshToken, accessToken and
// other items were gone. No key, no value.
After the last line, I could not interact with the web page anymore. If I refreshed the web page, I found I had signed out and had to sign in again.
It was still the same if I changed the storage for Auth from localStorage to sessionStorage.
Here are my questions:
Is this kind of behavior normal? Does Auth.updateUserAttributes() leads to a force sign-out?
If it's true, is there any way to avoid a mandatory sign-out?
If it's not, what's wrong with my code or configuration? Or should I do some specific configuration for the Cognito service?
Thanks a lot!
Well, I figured it out after reading the source code of aws-amplify.
Behind the call of Auth.userUpdateAttributes, amplify will finally call CognitoUser.refreshSession(refreshToken, callback, clientMetadata) (https://github.com/aws-amplify/amplify-js/blob/f28918b1ca1111f98c231c8ed6bccace9ad9e607/packages/amazon-cognito-identity-js/src/CognitoUser.js#L1446). Inside this function, amplify sends an 'InitiateAuth' request to Coginito. If an error of 'NotAuthorizedException' happens, amplify calls clearCachedUser() that delete everything I mentioned in my question from the localStorage.
There was an error of 'NotAuthorizedException' happening and reported by the network work monitor of Chrome Browser'. I thought it was generated after the sign-out-like behavior. However, it turned out to be triggered because no deviceKey was passed to the request's parameters.
So the whole story was:
I set remember device options in Cognito;
I used 'CUSTOM_AUTH' as the authentication flow type;
When a user successfully signed in to my application, Cognito didn't give the client the deviceKey because of the 'CUSTOM_AUTH' authentication flow type.
When Auth.userUpdateAttributes() was called, CognitoUser.refreshSession() was called behind it. It attached no deviceKey to Cognito when it sent ana request to asked Cognito to refresh the token. The Cognito rejected the request with an error of 'NotAuthorizedException'. The CognitoUser.refreshSession() handled the error and called clearCachedUser() to delete the stored tokens and other info from the localStorage.
My final solution is to turn off the remember device option since I have to use 'CUSTOM_AUTH' as the authentication flow type according to my application's functional requirements.
According to https://aws.amazon.com/premiumsupport/knowledge-center/cognito-user-pool-remembered-devices/, the remember device function only works when the authentication flow type is set as 'USER_SRP_AUTH'.

AWS Elemental MediaConvert Client SDK not working

Since I have fallen into the AWS trap and not ignoring the info message on the Elastic Transcoder Page saying that, we should start using Elemental MediaConverter instead, pretty much nothing is working as expected.
I have set up the Elemental MediaConvert following these steps. I have to admit that setting up everything in the console was pretty easy and I was soon able to transcode my videos which are stored on my S3 bucket.
Unfortunately, the time had to come when I was forced to do the transcoding from my web application, using the #aws-sdk/client-mediaconvert. Apart from not finding any docs on how to use the SDK, I cannot even successfully connect to the service, since apparently MediaConvert does not support CORS.
So my question is, did anyone use MediaConvert with the SDK successfully? And if yes, could you please tell me what to do?
Here is my configuration so far:
try {
const client = new MediaConvertClient({
region: params.region,
credentials: fromCognitoIdentityPool({
client: new CognitoIdentityClient({ region: params.region }),
identityPoolId: params.cognitoPoolId,
}),
});
const data = new CreateJobCommand({
JobTemplate: "Test Template",
Role: "Cognito_TestTranscodeUnauth_Role",
Settings: {
Inputs: [
{
FileInput: "s3://some-bucket/files/video.mp4",
},
],
},
});
return await client.send(data);
} catch (error) {}
I have used the template successfully within the console
The cognito role has all the rights necessary for operating MediaConvert and the S3
The input does exist and works in the console
If I just run the script I get a CORS not allowed error. However, if I disable CORS in my browser, I just get an Access denied error without further explanation. It's really driving me mad!
Any help appreciated!
So, after almost two entire days of trial and error plus digging into the source code, I finally found a solution! To make it short: unauthenticated access and MediaConvert will not work!
The entire problem is Cognito which does not allow access to MediaConvert operations on unauthenticated access. Here is the access list.
My solution
Since I am using Auth0 for my user authentication I was simply following this guide and basically all my problems were gone! To attach the token I was using
// React state variable
[token, setToken] = useState<string>();
// Using the Auth0 SDK to set the
const { getIdTokenClaims } = useAuth0();
getIdTokenClaims().then(j => setToken(j.__raw));
// use this in eg. useEffect hook
fromCognitoIdentityPool({
client: new CognitoIdentityClient({ region: region }),
identityPoolId: identyPoolId,
logins: {
"<your-auth0-domnain>": token,
},
});
It seems that if we have an authenticated user,
we have no CORS errors
we can attach any role to the federated Identity pool we want
I guess there are other solutions asa well and I'm convinced that if you use the built in user service from the AWS for your app authentication, you probably will have less problems here.
Anyway, hope this helps someone!
P.S: the example from the original question does work without problems!

Postman + NTLM Authentication + Authorization with claims + ASP.NET Core API = 403 Forbidden

We have an ASP.NET Core API that uses Windows Authentication and Claim based identity.
The API has one Controller with multiple Actions. The Actions have different authorization policies.
[Authorize(Policy = "Read")]
[HttpGet]
public async Task<ActionResult<Item>> Read()
{ ... }
[Authorize(Policy = "Write")]
[HttpPost]
public async Task<ActionResult<Item>> Write(Item item)
{ ... }
In Startup.cs we have this:
services.AddAuthorization(options => {
options.AddPolicy("Read", policy => policy.RequireClaim("OurReadType","OurReadValue"));
options.AddPolicy("Write", policy => policy.RequireClaim("OurWriteType","OurWriteValue"));
});
We also have a front end that consumes this API. Everything works fine when the front end application accesses our API. Users have only access to read actions if they have the read claim and the same goes for write actions. When a user that has only the read claim tries to call a write action they'll get a 401 Unauthorized. This is all expected behavior. No problems so far.
The problem starts when we try to access our API from Postman. ONLY from Postman do we get 403 Forbidden errors.
Postman is configured to use NTLM Authentication using my personal username and password. And my account has both read and write claims.
If we remove the [Authorize(Policy = "Read")] annotation from an action, we no longer get the 403 error when calling that action using Postman. This makes me think that the problem is somewhere with postman and claims based authorization.
Does anybody have an idea of what the problem is? I'm fairly new to claims based identity and to using Windows authentication to this extent. So any help is appreciated.

Amazon Cognito Need Something Explained

I am looking at the AWS Amplify and AWS Cognito documentation, and I need something explained. Take the following code:
Auth.signIn({
username, // Required, the username
password, // Optional, the password
validationData, // Optional, a random key-value pair map which can contain any key and will be passed to your PreAuthentication Lambda trigger as-is. It can be used to implement additional validations around authentication
}).then(user => console.log(user))
.catch(err => console.log(err));
Here is some more code from the documentation:
Auth.signUp({
username,
password,
attributes: {
email, // optional
phone_number, // optional - E.164 number convention
// other custom attributes
},
validationData: [] //optional
})
.then(data => console.log(data))
.catch(err => console.log(err));
My question is, where do the tokens get stored? Do you store them in state? If so, how do they get refreshed when they do. Or does Auth take care of this and you can just call auth when you need to. If so, do you have to wrap your entire ap with withAuthenticator? I don't understand this. Thanks!
Also, if you want a secure endpoint with AppSync, how does this work? Does it automatically check auth? You're not sending a token so I don't understand how this works. Thanks for your help!
If you're using Amplify it will keep the controls inside the LocalStorage. This will be managed by the Amplify Library. It is also responsible to request a new token when the first one is expired. Again: You don't need to worry about this, the library will manage this for you.
The thing is: you also must use Amplify to send your request o AppSync. When you use Amplify the library will see that you are making a request to a AWS Resource and you're logged in in Cognito and it will append the needed HTTP headers to the request before send it. All this will be done for you by the Amplify library. You can just use it..

AWS Cognito hosted UI and Amplify Auth with authorization code OAuth flow

I have a Vue.js webapp that I am trying to add simple authentication to using AWS Cognito and Amplify Auth. I have my user pool set up with "Authorization code grant" enabled for the OAuth flow. I also have the redirect URL set as https://example.auth.us-east-2.amazoncognito.com/login?response_type=code&client_id=XXXXXXXX&redirect_uri=https://example.com/auth/verify for the hosted UI.
This is what's within the page the hosted UI redirects to:
import { Auth } from "aws-amplify";
export default {
async created() {
try {
await Auth.currentSession();
} catch {
console.error("Not authorized");
}
}
}
When I sign in the first time through the hosted UI and am redirected, I get an error and am not recognized by Amplify as being authenticated. However if I sign in a second time, there is no error in the console and I have an authenticated session.
I do know that authorization code grant doesn't put the tokens in the URL, but I do see them in localstorage even on the first sign in. I have tried switching to using the "token" OAuth flow but the Amplify docs say the refresh token isn't provided that way and I'd like to not have sessions limited to 1 hour. Any guidance here?
For anyone facing the same problem, this seems to be a known issue.
The workaround is to subscribe to Hub actions and handle it there like
Hub.listen("auth", ({ payload: { event, data } }) => {
switch (event) {
case "signIn":
// signin actions
Auth.currentSession()
.then(user => console.log(user)) // redirect to default page
.error(err => console.log(err))
case "signOut":
// signout actions, redirect to '/' etc
case "customOAuthState":
// other changes
}
}
refer to https://github.com/aws-amplify/amplify-js/issues/5133#issuecomment-600759135