Amplify (JavaScript) - SAML login - javascript

I'm struggling for a few days now with AD SAML login in Amplify.
I've seen a few tutorials but my desired flow is different.
I don't have any button which will user click to trigger Auth sequence (and open popup).
Our users are going into a link:
https://....amazoncognito.com/login?response_type=token&client_id=...&redirect_uri=...
Which automatically redirect them into the front-end.
http://localhost:3000/#access_token=...&id_token=...&token_type=Bearer&expires_in=600
Which I can grab the access_token, id_token, token_type and expires_in from the query params.
I can do calls to our back-end with those tokens succesfully..
But what I am struggling with is make Amplify "login" sequence.. so it will identify the user and will create the localStorage values.. so for example when calling Auth.currentSession() it will return the token.
I've tried using Auth.federatedSignIn(..) but to no avail. I am not even sure if it's support this flow or not.
This is what I tried:
Auth.federatedSignIn(
'test-ad',
{
token: idToken,
expires_at: expiresIn,
},
{
name: "test-user",
},
);
But I keep getting
Unhandled Rejection (NotAuthorizedException): Invalid login token. Issuer doesn't match providerName
Which is strange as the providerName I'm passing ('test-ad') is the same as I configured in Cognito SAML Provider Name field (in Cognito -> User Pool -> Federation -> Identity Providers -> SAML -> new Provider)
I have my Amplify configured correctly
Amplify.configure({
Auth: {
identityPoolId: ...,
region: ...,
userPoolId: ...,
userPoolWebClientId: ...
}
});
And I can see the Chrome network call is with the correct payload:
{
"IdentityPoolId":"us-east-x:...",
"Logins":{
"test-ad":"...idToken..."
}
}
so - How can I authenticate into Amplify using token? is it even possible?
Any help will be appreciated!

Related

How do I make authenticated requests to Amplify GraphQL using Firebase Auth as OIDC?

I need help setting up Firebase Auth + Amplify GraphQL. I'm trying to log in using federated sign with securetoken.google.com/PROJECT-ID as the provider, and it seems to log in alright because when I call Auth.currentAuthenticatedUser() I get the token, and when listening to Hub "signIn" event I get the token. My problem is making authenticated requests to my GraphQL API.
const signIn = async () => {
try {
// already logged in using firebase so I just need to get the token from the current user
const tokenResult = await currentUser?.getIdTokenResult()
await Auth.federatedSignIn('securetoken.google.com/PROJECT-ID', {
token: tokenResult?.token,
})
const res = await Auth.currentAuthenticatedUser()
console.log('token', res.token) // eyjhxxxxxxxxxx...
} catch (error) {
// ...
}
}
const client = new AWSAppSyncClient({
url: AppSyncConfig.aws_appsync_graphqlEndpoint,
region: AppSyncConfig.aws_appsync_region,
auth: {
type: AppSyncConfig.aws_appsync_authenticationType,
jwtToken: () => getToken(),
},
})
const getToken = async () => {
const token = await Cache.getItem('#accessToken')
return token
}
When calling Auth.currentSession() I get "No current user". Also, I do see the token in the Authorization header when I attempt to fetch data.
I have had a similar issue so here are some things you can have a look at.
In the Appsync in the AWS Console
https://eu-west-1.console.aws.amazon.com/appsync/home
Make sure that your primary authorization mode is set to Open Id Connect, or add another authorization provider specifying "OpenId Connect" if you are happy with the primary.
If that does not solve it, you can try to add the #aws_oidc AppSync directive to your GraphQL schema.
type Query {
getPosts:[Post!]! #aws_oidc
}
or
type Post
#model
#auth(
rules: [
{ allow: owner, provider: oidc }
...
more here: https://aws.amazon.com/blogs/mobile/graphql-security-appsync-amplify/
Lastly, if you have more than one authorization provider, you might have to switch the primary authorization provider to "OpenId Connect" - the issue I had was that Cognito (primary) blocked my secondary API Key authorization provider.
Update
AWS uses IAM roles for everything related to security. So when you authenticate with whichever authentication provider an IAM role will be assigned to that request, and that IAM role needs permission on the resource in question, like execute permission on GraphQL queries, scanning of DynamoDB tables etc. as per this image:
So you might need specific rules set in the IAM console for the IAM role in question - or at least check that it has permission - if not, you'll also get an unauthorized error message in the Appsync GraphQL query console.
more here: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/WIF.html?icmpid=docs_ddb_console
and here: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/specifying-conditions.html?icmpid=docs_ddb_console
Try removing the cookie storage configuration in aws-exports.js may solve it. Maybe this helps you.
More discussion here Link-1 and Link-2

AWS Cognito login with Facebook

I have an Angular 10 site. I have an AWS Lambda (ASP.NET Core 3.1) that does authentication with AWS Cognito for users with email/password. But I want to allow users to also use Facebook (and eventually Google) to log in as well. In the Facebook/Google scenario my thought is not to allow access to AWS services directly for now (like S3, etc) but to interact with with my other lambda via a bearer token. I have a Cognito User Pool for which I created a Facebook identity provider and mappings. I read somewhere that I'd need an Identity Pool. So I created that and put in my Cognito user pool as a provider as well as Facebook.
Using the JavaScript code:
loginWithFacebook = () => {
const login$ = from(this.facebookService.login());
login$.subscribe(
(response: LoginResponse) => {
console.log(response);
this.facebookLoginToAWS(response);
},
error => {
console.error(error);
}
);
};
I can get a Facebook auth response no problem. Then using this code that you see on every blog, Stack Overflow post, and even in AWS's documentation (of course, substituting my own IdenityPoolId):
private facebookLoginToAWS = (facebookResponse: LoginResponse) => {
console.log('facebookLoginToAWS', facebookResponse);
if (facebookResponse.status === 'connected' && facebookResponse.authResponse) {
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
Logins: { 'graph.facebook.com': facebookResponse.authResponse.accessToken }
}, {
region: 'eu-west-1'
});
AWS.config.credentials.get((err) => {
if (err) {
return console.log("Error", err);
}
console.log("Cognito credentials", AWS.config.credentials);
console.log("Cognito Identity Id", AWS.config.credentials.identityId);
});
} else if (facebookResponse.status === 'not_authorized') {
document.getElementById('facebookStatus').innerHTML = 'Please log into this app.';
} else {
document.getElementById('facebookStatus').innerHTML = 'Please log into Facebook.';
}
};
I can get back a session token (in addition to a ton of other stuff like accesKeyId, identityId, and secretAccessKey).
But what do I do with that session token? Perhaps I'm confused, but I would think because there is a mapping between Facebook fields and Cognito fields, that somehow that Facebook user would be migrated into the Cognito user pool and I could get a JWT token for that user for my other lambdas. But after checking the AWS dashboard, I can see a log in (I think) in the Identity Pool but there is no corresponding User Pool entry.
Do I somehow have to manually migrate it over (using the AWS JavaScript SDK)? I don't want to use the Amplify library. Am I thinking about it wrong? Do I somehow use the session token as a bearer token with my other lambdas? Do I need to add that person as a user pool user somehow?

Okta JWT Verifier is not working

I want to verify the OKTA Access Token.
I created a simple use-case in nodejs, but I get permission error and I didn't find any solution to resolve this issue.
Here is my code:
const OktaJwtVerifier = require('#okta/jwt-verifier');
const issuer = '{{url}}';
const cid = '{{clientid}}'
const oktaJwtVerifier = new OktaJwtVerifier({
issuer: `http://${issuer}.com/oauth2/default`,
assertClaims: {
cid
}
})
oktaJwtVerifier.verifyAccessToken("accesstoken")
.then(jwt => {
// the token is valid
console.log(jwt.claims);
})
.catch(err => {
console.log(JSON.stingify(err))
});
And I get this error:
name: 'JwksError',
message:
{ errorCode: 'E0000006',
errorSummary: 'You do not have permission to perform the requested action',
errorLink: 'E0000006',
errorId: 'oaezeHaXzOCS9m4D5C5P9WnZA',
errorCauses: [] } } }
I checked the jwt-verifier github page and I think it should work.
The "default" authorization server also need API Access Management feature flag enabled. Do you have API Access management?
to check:
Go to Okta dashboard > security > API > check if you have tab named "Authorization servers" beside the "token" tab.
If you don't:
If your org. is a preview org. email developers#okta.com with your org. URL and they will enable it
If your org is not preview, you have to buy it.
alternatively, you can use Okta itself as an authorization server -> so the issuer would be "issuer: http://${issuer}.com" not "issuer: http://${issuer}.com/oauth2/default"
If last one is the case:
you CAN NOT validate the token locally.
However, you can send the token to /introspect endpoint to be validated OR request more information with the token from /userinfo
You can get more info. about above from developers.okta.com

Can't authenticate to cognito from backend as administrator

I need to get all users from my backend (Node.js). But when I am trying to authenticate I've got:
error AccessDeniedException, User ... assumed-role/Cognito_XXXUnauth_Role/ CognitoIdentityCredentials is not authorized to perform: cognito-idp:AdminInitiateAuth ...
My current code:
let cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider()
var params = {
AuthFlow: 'ADMIN_NO_SRP_AUTH',
ClientId: process.env.AWS_CLIENT_ID,
UserPoolId: process.env.AWS_USER_POOL_ID,
AuthParameters: {
USERNAME: '...'
PASSWORD: '...'
}
}
cognitoidentityserviceprovider.adminInitiateAuth(params, (err, result) => { ... })
It looks like I didn't log in since Cognito_XXXUnath_Role is used. Anyone had similar problem?
You are trying to use a Cognito Federated Identity credentials providers to create the CognitoIdentityServiceProvider service client. This will make the AWS SDK to call the Federated Identity service to obtain temporary AWS credentials which will be used to call cognitoidentityserviceprovider.adminInitiateAuth(...).
In this case, there are two possible solutions:
Use a different credentials provider to create the CognitoIdentityServiceProvider client with credentials which have access to call adminInitiateAuth API. Since you care doing this from backend, it should be safe to do so. This guide can help you with this.
If you must use the Cognito Federated Unauth role to create the service client, allow cognito-idp:AdminInitiateAuth in the unauthenticated role of the identity pool you are using to do this.
In case you are using Serverless framework, then the following settings should fix an error:
provider:
# you can add statements to the Lambda function's IAM Role here
iamRoleStatements:
- Effect: "Allow"
Action:
- "cognito-idp:AdminInitiateAuth"
Resource:
- "arn:aws:cognito-idp:*"

Skype Web SDK - Maintain a user session

I am using Skype Web SDK to get a user's contact list in the following manner.
Skype.initialize({
apiKey: 'a42fcebd-5b43-4b89-a065-74450fb91255',
}, function (api) {
var Application = api.application;
var client = new Application();
client.signInManager.signIn({
username: sip,
password: pwd
})
This works fine when I provide the username(sip) and password. However, when I reload the page, I have to provide the credentials again because the app re-initializes. Is there a way to maintain the user's sessions for a while after the initial login so that the page refreshes wouldn't need ask for credentials again?
I have looked through the samples and docuementation that Microsoft has and couldn't find a way. I've also tried to store the client object in the localStorage after the initialization and sign in, but when I tried to reuse the object from localStorage to get the contact list, it did not work.
http://officedev.github.io/skype-docs/Skype/WebSDK/model/api/interfaces/jcafe.signinmanager.html#signin last example explains that you can store oauth token and use it as unexpired token.
To connect to an existing app's event channel, specify id of that app:
sm.signIn({
username: "user1#company.com",
password: "password1",
id: "273867-234235-45346345634-345"
});
To sign in to Skype for Business Online using OAuth while handling the
logic of retrieving OAuth tokens yourself:
sm.signIn({
client_id: '123-456',
origins: [ 'https://webdir.online.lync.com/AutoDiscover/AutoDiscoverservice.svc/root' ],
cors: true,
get_oauth_token: function(resource) {
// Return a valid unexpired token for the specified resource if you already have one.
// Else, return a promise and resolve it once you have obtained a token.
return 'Bearer eyJ0e...';
}
});

Categories