I'm developing a REST API that will be accessed by customers, and I'd like to use AWS Cognito to handle authentication and authorization.
However, I want to abstract AWS Cognito from my customers, so that if I change from AWS Cognito to a different service, they don't have to change their code (just the API Key).
Unfortunately, after many days reading AWS's documentation, I couldn't find an answer to the following:
How can I give my clients a "personal access token" that never expires, that they can use to authenticate their scripts and back-end services, similar to what you can use for authenticating with GitHub?
So far, I created a REST Endpoint where clients can POST their username and password and I talk to AWS Cognito on their behalf, to do the authentication:
var request = new AdminInitiateAuthRequest
{
UserPoolId = "MyUserPoolId",
ClientId = "MyUserPoolAppClientId",
AuthFlow = AuthFlowType.ADMIN_NO_SRP_AUTH,
};
request.AuthParameters.Add("USERNAME", "myuser#mydomain.com");
request.AuthParameters.Add("PASSWORD", "123456"); // Relax, this is demo code
string accessToken = string.Empty;
var response = await _identityProvider.AdminInitiateAuthAsync(request);
var authResult = response.AuthenticationResult;
This all works, and authResult gives me 3 tokens that (I think) expire after 30 days (which I can configure up to 3650 days maximum).
This gives me 3 tokens (Access, ID, Refresh). I feel like giving these 3 tokens to the caller would be a bad choice as it's Cognito-specific...
How can I give my customers an API Key, that I can then use to authenticate them with Cognito?
Related
I'm writing a dropbox integration against my own account. When file get dropped I respond to a webhook notification and import the files into one of our backend systems.
It's all done in back end server code and there is no real opportunity to pop up a UI to get me to sign in.
I've developed it so far using the access token you can get from the app console but that expires after a few hours.
Are there any auth shortcuts when using the API with just your own account?
I've figured out some shortcuts for myself
First off all as its a background process it needs to be running with "offline" access and using refresh tokens to acquire short lived access tokens.
As there is no UI and its only for my own account I can get an authorisation code via the browser from this URL
https://www.dropbox.com/oauth2/authorize?client_id={{Your APP ID Here}}&token_access_type=offline&response_type=code
then use POSTMAN to get an access Token & Refresh Token:
In Postman:
Post to https://api.dropboxapi.com/oauth2/token
Authorisation Basic
UserName = {{AppKey}}
Password = {{AppSecret}}
Body :x-ww-form-urlencoded
code = <<Auth Code From Browser>>
grant_type = authorization_code
This produces a result with an access_token and a refresh_token
The refresh token will only expire if I withdraw access so that will be safe to add into my config and use to request access tokens or connect to the client.
In my case its c#
using (var dbx = new DropboxClient(refreshToken,appKey,appSecret))
{
var myAccount = await dbx.Users.GetCurrentAccountAsync();
String.Format("{0} - {1}",
myAccount.Name.DisplayName,
myAccount.Email)
.Dump("Account Details");
}
I am trying to figure out a way to create a JWT and sign it with the service account's private key and
Send the signed JWT in a request to the Google API Endpoint. I have search out there are numerous of the library available for Java and Python but is there any library available for PHP?
will need to follow Google’s Cloud Endpoints standard for authentication between services. Below there is an example of how we can access java, which I wanted to accomplish in PHP?
public static String generateJwt(final String saKeyfile, final String saEmail,
final String audience, final int expiryLength)
throws FileNotFoundException, IOException {
Date now = new Date();
Date expTime = new Date(System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(expiryLength));
// Build the JWT payload
JWTCreator.Builder token = JWT.create()
.withIssuedAt(now)
// Expires after 'expiraryLength' seconds
.withExpiresAt(expTime)
// Must match 'issuer' in the security configuration in your
// swagger spec (e.g. service account email)
.withIssuer(saEmail)
// Must be either your Endpoints service name, or match the value
// specified as the 'x-google-audience' in the OpenAPI document
.withAudience(audience)
// Subject and email should match the service account's email
.withSubject(saEmail)
.withClaim("email", saEmail);
// Sign the JWT with a service account
FileInputStream stream = new FileInputStream(saKeyfile);
GoogleCredential cred = GoogleCredential.fromStream(stream);
RSAPrivateKey key = (RSAPrivateKey) cred.getServiceAccountPrivateKey();
Algorithm algorithm = Algorithm.RSA256(null, key);
return token.sign(algorithm);
}
Using PHP to create authenticated request to an Google Endpoints
For PHP seems to not be a good solution since is not provided by the Google's Cloud documentation, as you can see here.
Nonetheless, there are some documentation regarding how you can use PHP within Cloud Endpoints via the JWT's client, as you can see here, and also here.
If neither of those fit your needs you can always use a custom method to authenticate users. As you know to authenticate a user, a client application must send a JSON Web Token (JWT) in the authorization header of the HTTP request to your backend API.
As a consequent, you could use the Extensible Service Proxy (ESP):
The Extensible Service Proxy (ESP) validates the token on behalf of your API, so you don't have to add any code in your API to process the authentication. However, you do need to configure your OpenAPI document to support your chosen authentication methods.
You could see how to implement custom method authentication for users here..
Finally, in case you are interested I link some other authentication methods that you could use with your Cloud Endpoints services in case none of the above fit your needs.
I hope it helps.
I have already implemented various REST-APIs using the Serverless Framework with APIG, DynamoDB as data storage and Cognito for user authentication with Angular2 as frontend. The functions can easily be secured by a Cognito authorizer on the server-side. The downside is that I have to integrate AWS SDK in my frontend applications in order to authenticate the user with Cognito first (signup/signin, ...). I could also use the AWS_IAM authorizer but then I also have to sign all requests on the client side with an AWS specific signature before sending the request to the API Gateway.
Now I was wondering if there is any possibility to keep authentication and authorization on the server side, so I can use an open standard like JSON Web Tokens for signup/signin? This would allow me to open my REST-API for other developers as well without forcing them to use Cognito at all.
I know that one possibility would be to implement a custom authorizer for my lambda functions but isn't there anything stable, which can be used "out-of-the-box" already? Most of the examples I found are using Cognito or IAM auth AWS signature signin on client side (e.g. serverless-stack.com).
It is curious that I didn't find any useful informations about this on the web so far, since I think that this is a typical use case for REST APIs. Or do I have a conceptual misunderstanding about API Gateway + Cognito?
I have been through the same trouble in understanding the way how AWS Cognito works and what options are available to implement authentication & authorization. Unfortunately there is no out-of-the-box method available to do it for your requirement. Nevertheless let's hope that Amazon comes up with a feature very soon.
Basically, there are 3 options available to implement authentication.
AWS_IAM
Cognito Authorizer
Custom Authorizer
AWS_IAM
In addition to authentication, this method can be used to implement authorization using IAM Roles or IAM Users easily. The only downside of it is that you need to send a request signed with an aws-signature-4 which is not the standard way that we have seen in IDP services like Auth0.
Cognito Authorizer
This method meets the expectation of sending a JWT token with API requests. You can create users in Cognito User Pool and then use it to authenticate and generate an IdToken. However, this method will only allow you to authenticate users; authorization needs to be handled in method level.
Custom Authorizer
This method can be used to write your own way of authentication and authorization. Also it helps to eliminate writing authorization logic in API methods. The ideal solution would be to use AWS Cognito User Pool to authenticate users and then generate a policy document for IAM Role to access resources.
Here is an example AWS cognito userpools JavaScript SDK get user's policy documents.
Also keep in mind that this solution will be invoking an extra lambda function for each request that you make.
You can use Cognito Auth to Server-side. Following would be the steps.
Implementing Sign-up and Sign-in
Implement Sign-up form in the frontend and API Gateway endpoint(e.g /register) using Lambda to receive, the Sign-up data, which will create user in Cognito using AWS SDK. For detailed reference check this link.
AWSCognito.config.region = 'us-east-1'; //This is required to derive the endpoint
var poolData = { UserPoolId : 'us-east-1_TcoKGbf7n',
ClientId : '4pe2usejqcdmhi0a25jp4b5sh3'
};
var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);
var attributeList = [];
var dataEmail = {
Name : 'email',
Value : 'email#mydomain.com'
};
var dataPhoneNumber = {
Name : 'phone_number',
Value : '+15555555555'
};
var attributeEmail = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserAttribute(dataEmail);
var attributePhoneNumber = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserAttribute(dataPhoneNumber);
attributeList.push(attributeEmail);
attributeList.push(attributePhoneNumber);
userPool.signUp('username', 'password', attributeList, null, function(err, result){
if (err) {
alert(err);
return;
}
cognitoUser = result.user;
console.log('user name is ' + cognitoUser.getUsername());
});
Do similarly for the Sign-in by creating an frontend & API Gateway endpoint(e.g /login)
var authenticationData = {
Username : 'username',
Password : 'password'
};
var authenticationDetails = new AWSCognito.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);
var poolData = { UserPoolId : 'us-east-1_TcoKGbf7n',
ClientId : '4pe2usejqcdmhi0a25jp4b5sh3'
};
var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);
var userData = {
Username : 'username',
Pool : userPool
};
var cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
console.log('access token + ' + result.getAccessToken().getJwtToken());
/* Use the idToken for Logins Map when Federating User Pools with Cognito Identity or when passing through an Authorization Header to an API Gateway Authorizer */
console.log('idToken + ' + result.idToken.jwtToken);
},
onFailure: function(err) {
alert(err);
},
});
Storing and sending the JWT from your Browser and Validating at API Gateway.
After receiving the JWT from the Sign-in API endpoint, you can store it locally in user's browser, using HTML5 Localstorage, Sessionstorage or Client Side Cookie. Optionally if you need to use Server Side Cookies, it will require to have a Proxy backend which keeps the Session state with the Web App and Convert it to the JWT to invoke the API Gateway.
From the Web Browser(Assuming your client directly invokes API Gateway) set a HTTP header called Authorization and forward the JWT to the API Gateway invocations.
At API Gateway use Cognito Authorizer to as the Authorize the token where it will also forward the user identity resolved to your Lambdas.
Note: Here I have purposefully avoided the IAM Authorization since it will require some additional work from Web App JavaScripts to implement Signature 4 Signing at Browser and also requires to refresh the token frequently which is straightforward with AWS JavaScript SDKs but will become complex if you need to implement it on your own.
Please take a look at this here.
The example demonstrates various configurations that include custom authorizers, cognito, lambda, dynamoDB etc.
I have developed token based spring security using JWT referring this project in git https://github.com/szerhusenBC/jwt-spring-security-demo. Now I need to get facebook login in my application. For social login, I found another web page https://ole.michelsen.dk/blog/social-signin-spa-jwt-server.html which explains how the social login must be carried out.
In the normal login, my JWT project creates a token based on username, password, expiry date and time of creation. Everytime the token comes, all values from above fields are retrieved and compared to authenticate the token and then served. I've two questions:
In the social login, there will be no password created. A token will be received from the facebook(my frontend does this). I have to verify if the token is valid or not. How am I supposed to do it in JWT?
After verifying as per the article I'm supposed to create my own token for future reference. Now, there is no password in facebook login. How do I create the token?
Let me know if there are any good site available for social login using JWT in spring boot applictaion.
I found myself in similar situation, and decided to follow a slightly different approach, delegating the responsibility of authenticating with FB to the server itself.
It provides an entry point: “/auth/facebook” that redirects to FBs and proceeds to the authentication.
After that it acquires the AccessToken for the logged user and creates a JWT Token that returns to the client.
Here is a blog post explaining how to use Spring Social Facebook and Spring Security for a similar case: Stateless Spring Security Part 3: JWT + Social Authentication
Consider removing the password field from your jwt. Facebook can supply you the email and name so use that for the payload. Here is my example.
userSchema.methods.generateJwt = function() {
var expiry = new Date();
expiry.setDate(expiry.getDate() + 7);
return jwt.sign(
{
_id: this._id,
email: this.email,
name: this.name,
exp: parseInt(expiry.getTime() / 1000)
},
jwt_secret
);
};
I largely followed this tutorial on how to do client authentication with Google Sign-In, get a JWT, and verify the JWT on the server on each request using Google's tokeninfo endpoint.
Everything works great except the tokens expire after 60 min and I don't know how to refresh the token.
The API server should not be involved in this refresh. The client is responsible for getting a refreshed token from the authentication service (google in this case) but there is no documentation on how to do that that I've found.
What I've observed is that the id_token is actually automatically updated by the gapi library before expires_at passes, usually about 5min before.
authInstance = gapi.auth2.getAuthInstance()
currentUser = authInstance.currentUser.get()
authResponse = currentUser.getAuthResponse()
You can then get the id_token and expired_at by doing:
authResponse.id_token
authResponse.expires_at
You can monitor for this update by doing:
authResponse.listen(function (gUser) {
const jwt = gUser.getAuthResponse().id_token
// ...you now have an updated jwt to send in all future API calls
}