I've created a Cognito User Pool. I can list the users and add the users using the AWSCognitoIdentityProviderClient from the Java AWS SDK.
However, I have a custom login page and I wish to take the entered username and password and authenticate against my User Pool. I don't see anywhere in the Java AWS SDK where I can pass credentials and get an authentication result from.
Edit: I can't get past this error:
NotAuthorizedException: Missing credentials in config
Relevant code:
AWS.config.region = 'us-east-1';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:087a3210-64f8-4dae-9e3c...' // your identity pool id here
});
AWSCognito.config.region = 'us-east-1';
AWSCognito.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:087a3210-64f8-4dae-9e3c...' // your identity pool id here
});
var poolData = {
UserPoolId: 'us-east-1_39RP...',
ClientId: 'ttsj9j5...',
ClientSecret: 'bkvkj9r8kl2ujrlu41c7krsb6r7nub2kb260gj3mgi...'
};
var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);
var authenticationData = {
Username: 'test#foo.com',
Password: 'foobarfoo',
};
var authenticationDetails = new AWSCognito.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);
var userData = {
Username: 'test#foo.com',
Pool: userPool
};
var cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
console.log('access token + ' + result.getAccessToken().getJwtToken());
},
onFailure: function (err) {
alert(err);
},
});
The AWS Java SDK includes APIs to authenticate users in a User Pool. You can authenticate a user using either the InitiateAuth api or AdminInitiateAuth api of the AWSCognitoIdentityProviderClient class. The difference between these two API is explained in the documentation. In short, for InitiateAuth, you need to perform SRP calculations and then pass it to the API, while in AdminInitiateAuth you can directly pass the username and password. You can read about the security implications in both cases and decide which one to use.
Documentation :
https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html
API reference:
https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_InitiateAuth.html
https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminInitiateAuth.html
My working sample(Groovy):
def login() {
AWSCognitoIdentityProviderClient client = new AWSCognitoIdentityProviderClient()
println("Provider client: " + client)
client.setRegion(Region.getRegion(Regions.AP_NORTHEAST_1))
HashMap authParams = new HashMap<>()
authParams.put("USERNAME", "User1")
authParams.put("PASSWORD", "a*123")
AdminInitiateAuthRequest adminInitiateAuthRequest = new AdminInitiateAuthRequest()
.withClientId(<YOUR_CLIENT_ID>)
.withUserPoolId(<YOUR_USER_POOL_ID>)
.withAuthFlow(AuthFlowType.ADMIN_NO_SRP_AUTH )
.withAuthParameters(authParams)
AdminInitiateAuthResult result = client.adminInitiateAuth(adminInitiateAuthRequest);
if (result != null) {
System.out.println("AdminInitiateAuthResult:");
System.out.println(result.toString());
} else {
System.out.println("No result available");
return;
}
}
Authentication is only supported via JavaScript, iOS and Android at this time. The necessary apis to authenticate are not part of the server SDKs (java, python et. all) during the beta. Using the JavaScript SDK is the recommended way of authenticating from your login page.
check here https://github.com/aws/amazon-cognito-identity-js
there is a missing line of code
This page http://docs.aws.amazon.com/cognito/latest/developerguide/using-amazon-cognito-user-identity-pools-javascript-examples.html is not updated
// Need to provide placeholder keys unless unauthorised user access is enabled for user pool
AWSCognito.config.update({accessKeyId: 'anything', secretAccessKey: 'anything'})
After including this I stopped having this error.
Related
I use Amazon Cognito Identity SDK for JavaScript to log in my users at www.mydomain.com.
I was wondering if it was possible to have my users automatically logged in on store.mydomain.com
For what i know Cognito stores the logintoken and other things in localstorage and that can't be reached from subdomains?
AWS Cognito JS SDK uses LocalStorage to store the authentication tokens by default. You can change this behavior and use cookies to store the tokens. Then you can use your parent domain name while setting the cookies and all your sub-domains can access this cookie.
To achieve this, you can use the CookieStorage class from the JS SDK while creating the CognitoUserPool objects.
TypeScript Code snippets below
Full implementation can be found here. A running application can be viewed here.
To authenticate the User (on the main domain, say, example.com) -
signIn(email: string, password: string): Observable<any> {
let userPool = new CognitoUserPool({
UserPoolId: environment._USER_POOL_ID,
ClientId: environment._CLIENT_ID,
Storage: new CookieStorage({secure: false, domain: "example.com"}),
});
let authenticationDetails = new AuthenticationDetails({
Username: email,
Password: password,
});
let userData = {
Username: email,
Pool: userPool,
Storage: new CookieStorage({secure: false, domain: "example.com"}),
};
let cognitoUser = new CognitoUser(userData);
return Observable.create((observer: Observer<any>) => {
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: result => {
observer.next(result);
observer.complete();
},
onFailure: error => observer.error(error),
});
});
}
To check if user is authenticated (on a subdomain, say, sub.example.com)
isAuthenticated(): Observable<boolean> {
let userPool = new CognitoUserPool({
UserPoolId: environment._USER_POOL_ID,
ClientId: environment._CLIENT_ID,
Storage: new CookieStorage({secure: false, domain: "example.com"}),
});
let cognitoUser = userPool.getCurrentUser();
if (cognitoUser != null) {
return Observable.create((observer: Observer<boolean>) => {
cognitoUser.getSession((error, session) => {
if (error) {
console.error(error);
observer.next(false);
observer.complete();
}
console.log(session, session.isValid(), session.isAuthenticated);
observer.next(session.isValid());
observer.complete();
});
})
}
The tokens for the Amazon Cognito Identity SDK for JavaScript are stored in local storage indeed. However, if you use the Cognito Hosted UI and the Cognito Auth SDK linked below, you can manage SSO.
The way that works, if you have tokens stored against the local storage of your domain, you can login. Otherwise, you hit our login endpoint and if there is a cookie stored against it, you get tokens in the response that will be stored in local storage and can be accessed with the Amazon Cognito Identity SDK (cause they are stored in the same location).
https://github.com/aws/amazon-cognito-auth-js
I am working with the Serverless Framework in my approach to Authentication. My goal is to create an API endpoint that triggers (via AWS API Gateway) a Lambda Function that creates a new AWS Cognito user. The endpoint will have a custom authorizer to protect it.
My Lambda function is below. When it's run, I receive the error "NotAuthorizedException: SignUp is not permitted for this user pool". Any thought on how to authorize my Lambda function to create a new user?
'use strict';
var AmazonCognitoIdentity = require('amazon-cognito-identity-js');
var CognitoUserPool = AmazonCognitoIdentity.CognitoUserPool;
var CognitoUserAttribute = AmazonCognitoIdentity.CognitoUserAttribute;
module.exports.init = (event, context, callback) => {
console.log('Lambda initiated with event:',event);
// Define AWS Cognito User Pool
var poolData = {
"UserPoolId": process.env['COGNITO_USER_POOL_ID'],
"ClientId": process.env['COGNITO_APP_CLIENT_ID']
};
var userPool = new CognitoUserPool(poolData);
console.log('userPool:',userPool);
// Define User Attributes
var attributeList = [];
var dataEmail = {
"Name": "email",
"Value": "email#example.com"
};
var attributeEmail = new CognitoUserAttribute(dataEmail);
attributeList.push(attributeEmail);
console.log('attributeList:',attributeList);
// Create User via AWS Cognito
userPool.signUp('username', 'password', attributeList, null, function(err, result) {
if(err) {
console.log('err:',err);
callback(err,null);
} else {
console.log('result:',result);
cognitoUser = result.user;
console.log('user name is ' + cognitoUser.getUsername());
callback(null,result);
}
});
};
"NotAuthorizedException: SignUp is not permitted for this user pool" exception is thrown when the user pool only allows administrators to create the users via the AdminCreateUser API. With this setting enabled, SignUp API cannot be called and will throw this error.
If you are calling this from a lambda trigger you can use AdminCreateUser API or disable this setting so your user pool allows SignUp API calls.
As Chean Mehta pointed out, you can disable the AdminCreateUser setting for SignUp API to work, for that you have to set AllowAdminCreateUserOnly to false in your serverless cognito configuration or you can disable this by following these steps:
Go to your cognito console.
Select your user pool.
Select Policies under General settings.
Select Allow users to sign themselves up
and Save changes
My setup consists of an AWS API Gateway with IAM access control and AWS cognito for log in.
I access the API already from an Android app and would now like to build a web app (angular2) to do the same.
On Android, I'm using the AWSCognitoCredentialsProvider to supply the API SDK with the required credential. (http://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-generate-sdk.html)
Unfortunately I cannot figure how / if I can do that with the javascript SDK?
I have no trouble using cognito to log in and get the session ID, access token etc. However, the API SDK requires me to provide accessKey and secretKey.
Here's the relevant code snippet from the generated API SDK:
var authType = 'NONE';
if (sigV4ClientConfig.accessKey !== undefined && sigV4ClientConfig.accessKey !== '' && sigV4ClientConfig.secretKey !== undefined && sigV4ClientConfig.secretKey !== '') {
authType = 'AWS_IAM';
}
In other words, I have this part working (from some example code):
static authenticate(username:string, password:string, callback:CognitoCallback) {
AWSCognito.config.update({accessKeyId: 'anything', secretAccessKey: 'anything'})
let authenticationData = {
Username: username,
Password: password,
};
let authenticationDetails = new AWSCognito.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);
let userData = {
Username: username,
Pool: CognitoUtil.getUserPool()
};
console.log("Authenticating the user");
let cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
console.log(AWS.config);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
callback.cognitoCallback(null, result);
},
onFailure: function (err) {
callback.cognitoCallback(err.message, null);
},
});
}
and now I'd like to use this:
this.apigClient = apigClientFactory.newClient({
accessKey: "anything",
secretAccessKey: "anything",
sessionToken: "nothing",
region: 'eu-central-1'
How do I get accessKey, secretAccessKey and sessionToken out of my AWSCognito? I was unable to find any API for that so far...
Thank you Bob, for pointing me in the right direction! I've now figured it out and thus for completeness sake, here's the full solution to my problem:
From the service that creates the apigClient:
return CognitoUtil.getCredentials()
.then(() =>
this.apigClient = apigClientFactory.newClient({
accessKey: AWS.config.credentials.accessKeyId,
secretKey: AWS.config.credentials.secretAccessKey,
sessionToken: AWS.config.credentials.sessionToken,
region: 'eu-central-1'}));
The getCredentials() method, which is key to get the required temporary credentials:
public static getCredentials():Promise{
return new Promise((resolve, reject) => {
CognitoUtil.getIdToken({
callback() {
},
callbackWithParam(idTokenJwt:any) {
let url = 'cognito-idp.' + CognitoUtil._REGION.toLowerCase() + '.amazonaws.com/' + CognitoUtil._USER_POOL_ID;
let logins = {};
logins[url] = idTokenJwt;
let params = {
IdentityPoolId: CognitoUtil._IDENTITY_POOL_ID, /* required */
Logins: logins
};
AWS.config.region = CognitoUtil._REGION;
AWS.config.credentials = new AWS.CognitoIdentityCredentials(params);
AWS.config.credentials.refresh(result => {
console.log(AWS.config.credentials);
resolve();
});
}
});
});
}
So the key insight here was, that
I authenticate to the user pool (shown in my question)
I use that with an identity provider to retrieve temporary credentials (getCredentials)
I use the temporary credentials out of AWS.config.credentials to setup the apigClient
I hope this is helpful to someone else as well. Certainly the code that I just posted probably could use some refactoring, so any comments on that are very welcome!
Cognito is actually made of 3 different services:
Cognito Your User Pools - What you've integrated here
Cognito Sync - For syncing user preference data for users
Cognito Federated Identity - For federating identities (FB, Google or User Pools) into your account and generating credentials.
What the API Gateway client is expecting is credentials that come from Cognito Federated Identity.
See the Cognito documentation for integrating your user pool with Cognito Federated Identity.
So I used this chunk of code found on the official repo of AWS JS SDK.
It is used to authenticate a user.
It is returning a blank response.
AWSCognito.config.region = 'us-east-1';
var authenticationData = {
Username : '+1112223333', //some phone number used as an Alias
Password : 'password123456',
};
var authenticationDetails = new AWSCognito.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);
var poolData = {
UserPoolId : 'us-east-1_P00l1d', // Your user pool id here
ClientId : 'xxx' // Your client id here
};
var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);
var userData = {
Username : 'myusername',
Pool : userPool
};
var cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
console.log('access token + ' + result.getAccessToken().getJwtToken());
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId : 'xxx', // your identity pool id here
Logins : {
// Change the key below according to the specific region your user pool is in.
'cognito-idp.pool_id_number_here_xxx' : result.getIdToken().getJwtToken()
}
});
},
onFailure: function(err) {
alert(err)
console.log("Error " + err);
},
});
So, for the authenticationData, I used the phone number as username (phone number is set as an alias) and the password. I tried with the username directly as well.
The UserPoolId and ClientId are correct as I used the same value for registering and confirming the phone number.
In the userData, I set the Username with the proper myusername.
About the Identity Pool, I created an Identity Pool on AWS Console linked to my App and my UserPool and I replaced the values IdentityPoolId and Logins in authenticateUser.
I am not completely sure about the value in Logins though. I used cognito-idp.POOLIDNUMBER.
The output from AWS is blank.
I am thinking that I can not even reach the server and I suspect an issue with the roles or the Identity Pool (the userPool is fine I suppose).
My identity pool is only using AWS Cognito users, not Facebook or other Identity Providers.
Recompiling SJCL with --with-all solved the issue.
Please make sure you have all the necessary libraries for 'Amazon Cognito Identity SDK for JavaScript'. Following is the list and links to these libraries.
1.The Amazon Cognito AWS SDK for JavaScript -
https://raw.githubusercontent.com/aws/amazon-cognito-identity-js/master/dist/aws-cognito-sdk.min.js
2.The Amazon Cognito Identity SDK for JavaScript -
https://raw.githubusercontent.com/aws/amazon-cognito-identity-js/master/dist/amazon-cognito-identity.min.js
3.JavaScript BN library (jsbn.js,jsbn2.js) -
http://www-cs-students.stanford.edu/~tjw/jsbn/
4.Stanford JavaScript Crypto Library -
https://github.com/bitwiseshiftleft/sjcl
5.AWS SDK for JavaScript (optional, to use other aws services) -
http://aws.amazon.com/sdk-for-browser/
Include all these libraries and then use the code snippet below.
AWSCognito.config.region = 'YOUR_REGION';
var poolData = {
UserPoolId : 'YOUR_USER_POOL_ID', // Your user pool id here
ClientId : 'YOUR_CLIENT_ID' // Your client id here
};
var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);
var userData = {
Username : "userName", // your username here
Pool : userPool
};
//Signing Users in the App
var authenticationData = {
Username : "userName", // your username here
Password : "password" // your password here
};
var authenticationDetails = new AWSCognito.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);
var cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
console.log('access token + ' + result.getAccessToken().getJwtToken());
},
onFailure: function(err) {
console.log(err);
}
});
I'm doing some web based app that shows user calendar from database entries. In this web app user can authenticate it's Google Calendar account, through JavaScript library. Then I want to share this access authorization with my desktop service. Desktop service is written in C#.
Does anybody knows how can I fill up CalendarService class in C# having following code?
Javascript:
function auth() {
var config = {
'client_id': "myID",
'scope': "https://www.googleapis.com/auth/calendar"
};
gapi.auth.authorize(config, function () {
console.log('login complete');
console.log(gapi.auth.getToken());
});
}
Then there is my C# code in Windows Service
var tokenResponse = new TokenResponse
{
AccessToken = tokenCopiedFromJavascriptObject,
Issued = propertyCopiedFromJavascriptObject,
TokenType = "Bearer",
Scope = CalendarService.Scope.Calendar,
RefreshToken = "3600"
};
var authorizationCodeFlowInitializer = new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets()
{
ClientId = myIdForDesktopApp,
ClientSecret = ClientSecretForDestopApp
},
Scopes = new[] { CalendarService.Scope.Calendar },
Clock = Google.Apis.Util.SystemClock.Default
};
var authorizationCodeFlow = new GoogleAuthorizationCodeFlow(authorizationCodeFlowInitializer);
_credentials = new UserCredential(authorizationCodeFlow, _calendarId, tokenResponse);
_service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = _credentials,
ApplicationName = "Fast and Easy Reservation System"
});
const string meeting = "Some fancy meeting";
var e = _service.Events.QuickAdd("primary", meeting).Execute();
}
Is there any hope to get this working? For example I have 2 apps (web and desktop) in my Google API Project. These are within one project so I think it should work as a hybrid app and they should share one authorization token.
Object returned by JS auth function have following properties:
access_token
client_id
expires_at
expires_in
issued_at
response_type
scope
token_type
I've made Controler in ASP.NET MVC application to handle Javascript code and send token to my database.
In order to avoid incompatible application types (web and native), my Windows Service uses client_secrets.json file from web type application. In order to precisely control flow of application and somehow cheat that native service is web application, I have loaded just client_id and client_secret from client_secrets.json (web).
var flow = new GoogleAuthorizationCodeFlow(
new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets()
{
ClientId = "some-numbersAndLettersxxxxxxxxxx.apps.googleusercontent.com",
ClientSecret = "xxxxxxxxxxxxxx"
}
});
Then, I loaded token from database in my app and assigned it.
var token = new TokenResponse
{
RefreshToken = "ya29.UAH2llQasSADfga32fWay3aqi1pb0AUwAWy_WzbPNyNkYCRPhe5_zBbEG94TZafV8pBMHzC8Q"
};
After well prepared flow and token I was able to create credentials object.
var credential = new UserCredential(flow, clientId, token);
Now, native app pretends to be web application and it can access all user data without additional consent screens.