Im learning about JWT, but i dont know how to manage the tokens (ACCESS token and REFRESH token) in the front end for making HTTP requests.
An example, when i log in in my page, i make a login request to the server that gives me an ACCESS token and a REFRESH token (that a save in the cookies and in the user data base).
Now, for make some other HTTP request that needs to be authenticated, how can i transfer the ACCESS token to the Authorization Header of the request? Just put it on a variable? it is safe?
Also consider that my web site have multi-pages, how can i pass the token over the diferents pages?
Another strategy that cames to my mind is to use the refresh token that i have on my Cookies (once that i logged in) to make a new access token, but i dont know how is the standard manage of this.
Sorry if im not being clear...
Thank you!!
I'm assuming you're using axios,
You could create your apiInstance like this
export const apiInstance = axios.create({
baseURL: YOUR_BASE_URL,
responseType: 'json',
headers: {
//Content-Type: 'CONTENT_TYPE',
// you dont need to pre-define all headers
},
})
To save the access token for further use
apiInstance.defaults.headers.common['Authorization'] = `Bearer ${token_here}`
Then every time you want to make a request you just use apiInstance.get/put/patch etc. The access token is saved and reusable. If it expires, you're gonna have to refresh it, in which case you need to have your refresh token saved somewhere, local storage is not recommended but could work as a start.
Related
I am trying to list my GitHub public repos on my public website using their REST API. I have been creating personal tokens so far. In localhost it works.
When I do the request from my site it responds a 401 and it deletes my token.
To get the repos I am doing this simple request:
async function loadRepositories() {
const token = 'my_token';
const response = await fetch("https://api.github.com/users/tauromachian/repos",
{
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
}
);
const repositories = await response.json();
return repositories;
},
How can I get this done?
Github is trying to protect you from someone stealing your identity here. There are ways to detect if a HTTP request is sent from the browser or a server (it's not 100% reliable though).
As you make the request from the browser which is a public client, meaning everyone can read the code and hence secrets you use within the code you are exposing your Personal Access Token (PAT). This PAT identifies you against Github, therefore if someone else could get hold of it, they can impersonate you and e.g. delete repos, steal code etc. (if the token has the correct scopes).
As Github wants to prevent that from happening, they delete tokens which are publicly exposed (they know it's exposed as the the request comes from a browser). Therefore attacks like that are not possible anymore.
To make your website work however you can simply make the request from your server as you can securely store secrets on the server-side and call the endpoint of your server from your website. Once you do that, Github won't delete the token, your token is save and you can display the data on the website.
I need to redirect to another website and send the JWT as a bearer token as well.
Can I use fetch to do it?
Something like this:
$( document ).ready( function() {
document.getElementById('Aother-Website-Link').addEventListener('click', event => {
event.preventDefault();
fetch('Another-Website-Link',
{
mode: "cors",
method: "GET",
headers: {
'Authorization': `Bearer ${jwt}`
}
})
.then(() => {
window.location.href = 'Another-Website-Link';
})
.catch(error => {
console.error(error);
});
});
No, you can't do this. First, cookies cannot be transfered nor set across different domains, cookies can only be set if the server is sitting on the same domain or a subdomain.
If your website is website1.com and you make a fetch request to backend2.com, backend2.com cannot set any cookies for your website. If you wanted to set cookies on your website, your website should either make a fetch request to the same domain (website1.com/api/something) or a subdomain like api.website1.com.
With this in mind, no, you cannot redirect and transfer cookies to another website.
If you have access to the site there might be a solution. You can set up a custom route on the site you want to redirect to, call it something like /redirectCallback or something like that, and then you can redirect the user and either pass in your cookies via an url query (/redirect callback?cookie1Val=...) so that the website you want to redirect to can parse those values, send them to their server and then their server can set the same cookies, or, if you want more security and you also happen to have a database set up, you can create a temporary switchSession with and id and cookieValue fields on your server right before you redirect and send it's id via url query (/redirectCallback?redirectSessionId=...) and then the website can again parse it, send it to their server, their server will parse the data on your database then delete the redirectSession and send your cookies up to the client again (You can implement signing/veryfing if you want even more security).
I hope it helped!
Edit: spelling.
I am creating a login page. The idea is to take the username and password then use that to get an id token from AWS cognito user pool.
Then I need to store the token in a database and transfer that token to index.html along with the username. This is because index.html needs the token to make a post request to an API gateway using the same id token and it needs to know the user name to keep track of which user is currently logged in.
Now the index.html has to check if the token transferred to it matches the token in the database or not.
If it does and the token is not expired, then there is no redirection, otherwise, the index.html redirects to login.html.
The problem is my back-end completely relies on Amazon Lambda functions and API gateways because my company does not want me to use any back-end language. Now, I found that lambda functions could not set browser cookies nor read browser cookies.
This left me no choice but to use javascript cookies, sessions and local storage to transfer the token and username to index.html. However, this approach is considered to be insecure.
There aren't any tutorials on a secure login system using cognito.
Note: Any other secure login system will do the work, but remember I need to pass username and password to user pool to get an ID token which means, if I use any other login method, then I would now need to pass username and password both to index so that index can make a request to user pool to get an id token and make a request to the API gateway.
Now my question is, is there a way to actually transfer data to index from login without compromising security? Can I use lambdas in any other way to transfer the tokens?
Please help. Thanks in advance.
Now, I found that lambda functions could not set browser cookies nor read browser cookies.
I don't think this is strictly true.
Using Lambda Proxy Integration gives your Lambda function visibility into and control over many aspects of the HTTP request. This includes setting and reading arbitrary headers (including set-cookie).
To demonstrate, you can set a function like this up and attach an API Gateway trigger (with Lambda Proxy Integration enabled) to it:
exports.handler = (event, context, callback) => {
var returnobj = {
"statusCode": 200,
"headers": {
"Content-Type": "application/json", "access-control-allow-origin": "*",
"Set-Cookie": "testcook=testval; path=/; domain=xxxxxxxxxx.execute-api.us-east-1.amazonaws.com; secure; HttpOnly"
},
"body": JSON.stringify({})
};
console.log("headers", event.headers.Cookie);
callback(null, returnobj);
};
This function returns a Set-Cookie header that the browser will respect and send along with future requests to this domain. If you hit this in a browser twice, you'll see the cookie sent by the browser and logged by the lambda on the second request.
If you aren't willing to use Lambda Proxy Integration, you can probably still pull this off by mapping part of your Lambda response to a header in API Gateway.
I have created two Mean.io apps in domain.com and in sub.domain.com respectively and everything works as expected in both but the problem is that the one in the subdomain (sub.domain.com) needs to know if the user is logged in the main app (domain.com).
I know that passport handles sessions and knows if user is logged in because it creates an user object in req for every request in express.js:
if (req.user) {
// logged in
} else {
// not logged in
}
The inconvenient here is that this approach works from within the domain but not outside. In other words, if I make a request to backend like this:
$http.get('/api/users/me').success(this.onIdentity.bind(this));
from domain.com, this will be populated with user data, but if I make the same request directly from the browser, for example, it returns null.
I need to understand how could I pass this information across domains? And if everytime this request $http.get('/api/users/me').success(this.onIdentity.bind(this)); is made, information is passed to backend?
I found the answer after some deep research.
Short answer: it is impossible using localStorage (data is only accesible by domain; not even for subdomains), which is the tool Mean.io now uses to store user information.
Long answer, every time, as long as you are logged in, you send a request to backend, angular intercepts the request before actually send it (this post explains this https://auth0.com/blog/2014/01/07/angularjs-authentication-with-cookies-vs-token/) and adds an authorization header like this:
headers:
{
...
authorization: 'Bearer eyJhbGciOiJIUzI1NiJ9.JTdCJTIyX2lkJTIyOiUyMjU1ZDFjYmIxNDA..._rUsUBFxCQy3qqUGi9QGVD0YXCEk0',
...
}
which passport later employs to serialize user info into session and put it in req.user. The Bearer token is stored in localStorage and for that reason it's impossible to get it from outside the domain. The only way I came up with was with cookies with domain = '.domain.com' so that every subdomain could read those cookies.
I'm attempting to get and store an access token from the Pocket API using Node.js. I am able to get the request token, redirect to the Pocket login page, redirect back to my site, and finally able to exchange the request token for an access token.
But that's where my problem lays. I don't know how I should go about actually storing the token, and without it I am unable to make API calls (of course). Here's the relevant code:
//called when Pocket API redirects back to /getAccessToken
function getAccessToken(response, requestToken) {
restler.post("https://getpocket.com/v3/oauth/authorize", {
headers: { "Content-Type" : "application/json",
"X-Accept" : "application/json" },
data : JSON.stringify({consumer_key:CONSUMER_KEY,code:requestToken})
}).on("complete", function(data, res) {
if(res.statusCode == 200) {
var accessToken = data.access_token;
console.log("Access granted: " + accessToken);
//BUT HOW DO I STORE THE ACCESS TOKEN FOR USE OF API CALLS ??
}
response.writeHead(307, {Location: DNS}); //go back to site
response.end();
});
};
I was thinking I should store the accessToken on the client side, but I don't actually know how to go about doing that. I've tried using cookies, but that didn't seem to work. Of course, I may have just implemented them wrong.
Your help is much appreciated.
How you store the access token usually depends on where you will be using the API.
I usually like to persist tokens in the database (like MongoDB) on the User document they are associated with, and then my web client can ping my server (via a RESTful endpoint) if it needs the token for anything. This way if the user clears all that state on the browser you don't have to go through the entire OAuth flow again.
you should probably make the cookies thing work. option 2 is to use localStorage but if you're struggling with cookies i wouldn't try going down that path - it gives you more control of when the tokens are sent across the wire but also requires a lot more work to make your serverside and clientside code coordinate.