I have an AWS API, with access controlled by an IAM authoriser associated with the unauthenticated role of a Cognito Identity pool. A successful call to the API invokes a Lambda function.
The current process is:
User obtains temporary security credentials. This includes:
User IdentityId e.g. eu-west-2:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX. This is the information I require in my Lambda function - see below.
Access credentials: AccessKeyId, SecretKey and SessionToken
The user calls the API using a signed request. The header of the request includes X-Amz-Security-Token, obtained from step 1
If authorisation is successful, the request is sent on to the Lambda function. event.headers includes X-Amz-Security-Token
Is it possible for the Lambda function to identify the identity pool ID of the caller (e.g. eu-west-2:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX), either from the data available in the header of the request, or any other means.
Things I have tried:
sts.getCallerIdentity with Invoke with caller credentials ticked in API Gateway
Looking at SessionToken, though this appears only to be a temporary access token rather than containing any useful data like a JWT.
If absolutely necessary I can include the identity ID of the user with every request, but I'd prefer to avoid this if possible.
The Cognito ID of the user is included in the requestContext property of the event object passed to your Lambda function.
E.g. the Identity of the Cognito user that access the API is:
event['requestContext']['identity']['cognitoIdentityId'];
Related
I'm trying to figure out how to use the token event as specified in Google's OAuth 2.0 documentation for server side applications (using Node.js):
Here's the code for the token event:
oauth2Client.on('tokens', (tokens) => {
if (tokens.refresh_token) {
// store the refresh_token in your secure persistent database
console.log(tokens.refresh_token);
}
console.log(tokens.access_token);
});
I actually get this event to fire, but am unsure about when to check for a valid token. If I do this one every request (let's say Google Docs API request), the token event fires after I'm requesting the API, so credentials are not set correctly.
How would you go about making sure that the correct access token is set BEFORE making a request?
Google says "Once the client has a refresh token, access tokens will be acquired and refreshed automatically in the next call to the API.", which is also kind of the case, but too late.
So my question once again: How do I, in my call, wait for the token event to finish updating my access token in order to set credentials BEFORE making the request?
Thank you!
I have values ββin the url like this
console.log("url:",url)
url: demo://app?accessToken=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm92aWRlciI6Imtha2FvIiwiaWF0IjoxNjE4MzIwOTg1fQ.Ver4V9lOBamkgU-DQC91LnVZLuvkLOGgytXPnkOWsFg
I want to parse the token in the url and put it in the maintoken.
how can i do that?
this is my code
useEffect(()=>{
Linking.addEventListener("url", ({url}) => {
console.log("url:",url)
const maintoken = parseToken(url);
console.log("token:",token);
})
return () => Linking.removeEventListener("url");
},[])
if the url always start with demo://app?accessToken= you can just do a substring.
const mainToken = url.subString(22);
I'm not really sure what you are trying to implement. But, if you are trying to extract values from a JWT and adding them to another signed JWT on the frontend, then it's not possible.
Tampering or trying to change the values in a hashed JWT token makes it an invalid token.
The whole objective of using a JWT token is to ensure nobody can tamper with the token and/or the data contained in it. Once a JWT is signed by whichever JWT module you are using, its signed with the purpose of making sure the data cannot be changed.
Because if you could change the values in the JWT and the JWT still passes as a valid token, then that would compromise your entire authentication process.
Also, don't append JWTs to URLs. It's bad practice. If you have a logging system in place that records all API calls, then your tokens will be recorded in the logs, because URLs are usually saved in logs. Anyone with access to the logs can decode the token and use the payload in the token for malicious reasons. Use HTTP headers instead.
What you should do
If you have to decode the URL on frontend and have no other option, use jwt_decode it will return the payload hashed in the JWT.
Send those decoded values to your server.
Sign a new access token on the server, use those values as payload in the access token.
Send back the newly signed access token to the frontend for use.
If the URL token is something that was created by you on your own server. You don't even need jwt_decode. Just use the methods that come with the JWT module you are using to verify the token. In the callback, you will get the payload that was attached to that particular token, use the payload to create a new access token and send that to the frontend.
I have a simple Lambda function which sends emails through SES. I can call it using a POST request with the required data and it will send an email. My question is, what are the methods I can use to secure this function? Currently, anyone can call that endpoint and execute the function with any data.
You need to set an authorizer for your API Gateway. This tutorial is a great start point.
In summary, you need to:
Create a Cognito User Pool
Create a Cognito Identity Pool that uses this User Pool
Make the client to log in and retrieve Cognito credentials
Make the client to send authorization headers for all requests
Set an authorizer in your Lamba function
Your serverless.yml will look like this with the authorizer configuration:
functions:
hello:
handler: handler.hello
events:
- http:
path: hello
method: post
authorizer:
arn: YOUR_USER_POOL_ARN
You don't need to be restricted to a Cognito authorizer. You can use configure an authorizer for Google+, Facebook, etc.
This setting means that the Lamba function will be triggered only by authenticated users and you can identify what is the User ID by inspecting the event object:
event.requestContext.authorizer.claims.sub
I am using the OAuth server flow for Google.
It starts with the user clicking a link that runs javascript to open a popup with the following request in the URI which is all working great:
var endpoint = "https://accounts.google.com/o/oauth2/auth";
endpoint = endpoint + "?scope="+encodeURIComponent(googlecalendar.SCOPES);
endpoint = endpoint + "&redirect_uri="+encodeURIComponent("https://myserver/google/");
endpoint = endpoint + "&response_type=code";
endpoint = endpoint + "&access_type=offline";
endpoint = endpoint + "&approval_prompt=force";
endpoint = endpoint + "&client_id="+encodeURIComponent(googlecalendar.CLIENT_ID);
endpoint = endpoint + "&state="+encodeURIComponent(googlecalendar.USER_ID);
On the server side, I get the state which contains the user_id for my DB and the authorisation code.
Now I want to exchange the authorisation code for access token (and renew token). This will be a HTTP request with a redirect URI, no state parameter is included.
The problem is that when I get those, I will need to store them against a user in my DB, but I don't have any way to check which user the callback is for.
The best I was able to come up with is using the token to query the google user's identity it belongs to but this still won't help me to find the user in the DB.
Can anyone help me with this flow? There must be some way to do. I don't want to use client libraries because later when I need to create watchers the PHP client library does not include this for the calendar API.
Short Answer
Despite the presence of a redirect parameter, the access token will generate a standard 200 response, not a 301 redirect. Depending on how you issue and handle the request/response, you can preserve your state.
More Detailed Answer
According to section 4.1.4 of the OAuth 2.0 spec document (RFC 6749), the response to an Access Token Request should be an "HTTP/1.1 200 OK".
In other words, the server will not perform a redirect, meaning you can issue a request and process the response in the same scope (either in the client or server, whatever your situation), so your database user ID need only be in local memory.
This is different from the Authorization Request, which is supposed to result in an "HTTP/1.1 302 Found" (redirect). See section 4.1.2.
So why is the redirect_uri parameter required?
According to section 4.1.3, the server must:
ensure that the "redirect_uri" parameter is present if the "redirect_uri" parameter was included in the initial authorization request as described in Section 4.1.1, and if included ensure that their values are identical.
In other words, the redirect_uri acts as a sort of secret or password which the server must use to verify the access token request. If the client fails to provide a redirect_uri parameter, or the parameter value is different from the original request, then the server must reject the access token request.
In my application,while registering the users i am saving username,password and jwt generated token with these fields in MONGO DB.When user tries to login with correct credentials then i will send the response with stored token.Then at client side(In my controller) i am using the localstorage to store the token so that i can send the same token for each and every request sent by the client.But I found some issues regarding this procedure:
I am generating same token for one user every time.So if any third person is able to get the token then he can access the restricted page.
Am i wasting space in db by storing the generated token in MONGODB
Can Anyone access the token stored in localstorage other than the user.
for each and every request in my single page application,I am again querying mongodb to get the token for that user and validating.Here,I am checking both client side and server side.
I am using jwt to generate tokens,Node,Express,Mongoose in my application
Am i following the good procedure.If not,can you please provide the solution for my approach or any new approach.
I have searched many sites for token based authorization and session based authorization,But nothing worked for me.
Note:I am beginner for Nodejs,AngularjS
You should store token in advanced key-value cache tool like: Redis
That would improve performance remarkably.
You will get token from database for 1st time then it should be stored in Redis. I used to set token as key and username as value. Next request , the token will be given from cache. with Redis you can set expire for token.
When a user registers, you would need to generate a JWT like you're doing now. That's OK. You don't need to save it to the database however. You didn't ask but I assume that the password should not be stored in clear text. You can use something bcrypt to encrypt before saving it to the database.
When user tries to login with correct credentials then i will send the response with stored token
Yes, that's correct way to do.
Then at client side(In my controller) i am using the localstorage to store the token so that i can send the same token for each and every request sent by the client.
Yes, on the client side, you can save the JWT to local storage and send it in subsequent requests to the server.
Now your bullet points:
So that you won't have the same JWT each time, you can include an "exp" claim in the payload (I'm assuming you're using something like jwt-simple to generate a JWT). Something like:
var payload = {
sub: account.username,
exp: moment().add(10, 'days').unix()
};
var token = jwt.encode(payload, "secret");
You don't need to store the JWTs in the database. In some cases, the token issuers (the authorization servers) are not the same as the resource servers. The resource servers only receives the JWTs in a request but there's no way for the resource servers to touch the database used by the authorization servers. Side note: If you eventually need to support refresh tokens, i.e. the JWTs that you hand to the clients will need to eventually expire, then you can store the refresh token in a database. Refresh tokens are not the same as JWTs (access tokens). The complexity to support refresh tokens will increase.
Local storage is not where you store passwords, but it can be used to store JWTs. For that very reason, a JWT must and should expire after a certain time.
Not sure what you mean by saying you check both client side and server side. When the client needs to access a resource (again it's fair to assume that the resource server might not be the same as the authorization server), the only thing that the resource server is passed is the JWT. Anyone can decode a JWT. For example, try to paste your JWT on this site http://jwt.io/. That's why a JWT should not contain any sensitive data. But if the resource server knows the secret that the authorization server uses when it encode the JWT, the resource server can verify the signature. Back to your third bullet, that's why it's OK to store the JWT in local storage of the client.
Update I'm updating this to answer to some of your questions in the comment box.
User clicks on 'Login' button triggers the Angular controller to post a request to the server, something like:
$http.post(url, {
username: $scope.username,
password: $scope.password
}).success(function(res) { ... })
Server receives the POST request, it checks username/password, then it generates a JWT, and sends back to the browser. Note that it does not have to save the JWT to the database. The code would be something like
var payload = {
sub: account.username,
exp: moment().add(10, 'days').unix()
};
var token = jwt.encode(payload, "secret");
res.status(200).json({
token: token
});
Back on the client side, in the success() callback above, now you can save the JWT in local storage:
.success(function(res) { $window.localStorage.setItem('accessJWT', res.token) })
The user is now authenticated. Now when user wants to access a protected resource, user don't have to provide username/password. With the JWT which can be retrieved from local storage, the client can now put the JWT in the Authorization header of the request using the bearer scheme, and sends the request to the server. In code, it would like:
headers.Authorization = 'Bearer ' + token;
The server receives the request. Again, this server receiving this request does not have to be the same as the server which generates the JWT above. The 2 servers can be in 2 different continents. Even if you save the JWT above, that does not do any good to this server which can not access the database where the JWT is stored. But this server can pull out the bearer token from the header of the request, validates the token and carries on with the normal tasks.
Hope this helps.
You do not want to store the JWT in mongoose because it appears in headers when logging in. You first generate a token then hash it using a module like crypto.
There are different ways to do this and they all use Passport which handles the tokens. Here's an example project Satellizer
I would recommend you generate the angular-fullstack project. Then go through the server/auth folder and the client/account folder. You will see how to securely handle authentication in a MEAN based app.