I create project using React + Redux + Apollo Client + Graphql
When we need to log in in our app we need to use token (saved in localStorage for example) which is put in the headers parameter like in the code below:
const client = new ApolloClient ({
uri: 'http://localhost:4000/api',
headers: {
authorization: `Bearer ${localStorage.token}`,
},
});
After request server verifies token and becomes aware who is the user.
My question: from where do we need to get token and put it to the headers parameter for log on (sign up) process? A new customer comes to our log on page, he has no token (in localStorage or somewhere else) at the beginning but server requires it in the requests. And if we remove headers parameter from our client, the log on process will proceed but server won't understand who is the current user.
Typically the server would be the one issuing the JWT token, and this would happen during user login or maybe during account creation. For these particular calls, you should not be expecting the JWT in the header. Instead, the user would be passing credentials, such as username and password. For most other calls, it is appropriate to pass the JWT in the header of the request.
Keep in mind that the main purpose of the JWT is free the user from having to provide personal credentials during most requests. Instead, the user can just present a JWT, much as one would present a passport, to get access to your web services.
In response to your comments below, I would suggest that you keep the signup/registration process separate from the user-only area of your application. Here is a typical workflow:
Prospective user visits your site, and creates an account, by choosing a username and password, and possibly by providing certain other personal information
Your application creates an account, and then sends an email verification link to the user's email address. The server lands the user on a page which mentions all of this
The user opens the email, which contains a verification link, which when clicked will activate the account. Your application returns a web page which then asks the user to login.
Finally, the user logs in from the normal login page.
Note carefully here, that JWT were not at all involved in the signup process, nor do they need to be. The user JWT only needs to come into existence after the user actually logs in for the first time.
Decision:
you need to check for token in localStorage and update the request if token exists
const client = new ApolloClient({
uri: 'http://localhost:4000/api',
request (operation) {
const headers = {};
const token = localStorage.getItem('token');
if (token) headers.authorization = 'Bearer ' + token;
operation.setContext({ headers });
}
})
Related
I am currently working on a medium scale app and am a month into learning React. I got to the part where I need to authenticate users. I have written some code and It is working, but I don't know is it secure enough. When my users login, they are assigned with a JWT token like this:
await axios.post(APIbase + '/login', {
username: username, password: password
}).then(res=>{
const token = res.data.token;
localStorage.setItem('token', token);
}).catch(err => {
console.log(err);
});
And then, when the user makes a request to a server it send the token by an auth header like this:
const token = localStorage.getItem('token');
const headers = { Authorization: `Bearer ${token}`};
const detailResult= await axios.get(API.base + API.details, {
headers:headers});
Is this safe enough? I heard that this is a not really a good practice, but I am not sure what exactly should I do.
Local storage is generally used for this kind of token, but keep in mind any JS on the page can access local storage. If you have any 3rd party code, it can get to the token by simply reading the local storage.
If you want a bit more secure way of storing it, you can use HTTPonly, secure cookie. That way it will not be accessible by JS and it will also be sent automatically in any request to the API, but it requires changes on the server to implement cookies instead of Authorization header.
You can also use a BFF (backend for frontend) approach with a server handling session then you don't need to store the token on the client side either (and only store in on BFF linked to the session), but keep using it for requests to the API from the BFF.
Security is a complex field and has a lot of trade-offs. There is no one correct answer for every use case.
This does really belong to react domain but is a more beta question and there is a special stack exchange for this: https://security.stackexchange.com/
I just wanted to know how can we validate the azure ad access token in a backend API in my case i.e. Django rest framework.
Consider that I have a single page app or a native app and a backend API (django rest framework) completely independen of each other. In my case if my single page app/native app wants to access certain data from the backend API, and inorder to access the API, user should be logged in the backend API.
So what my approch is to make use of MSAL library to get the access token from the SPA/native app and then once token is acquired, pass that token to backend API, validate it, get the user info from graph api. If user exists in the DB then login the user and pass the required info. If user info doesn't exist then create the user, login and pass the info from the API.
So my question is when I pass the access token to my backend api, how can I validate that the token that a user/SPA/native app has passed to backend API is valid token or not?
Is it just we need to make an API call to graph API endpoint with accessToken that user/SPA/native passed and if it is able to get the user data with the accessToken then then token is valid or if it fails then the accessToken is invalid.
Is it the general way to validate the token or some better approach is there? Please help
Good day sir, I wanna share some of my ideas here and I know it's not a solution but it's too long for a comment.
I created a SPA before which used msal.js to make users sign in and generate access token to call graph api, you must know here that when you generate the access token you need to set the scope of the target api, for example, you wanna call 'graph.microsoft.com/v1.0/me', you need a token with the scope 'User.Read, User.ReadWrite' and you also need to add delegated api permission to the azure app.
So as the custom api of your own backend program. I created a springboot api which will return 'hello world' if I call 'localhost:8080/hello', if I wanna my api protected by azure ad, I need to add a filter to validate all the request if has a valid access token. So I need to find a jwt library to decode the token in request head and check if it has a token, if the token has expired and whether the token has the correct scope. So here, which scope is the correct scope? It's decided by the api you exposed in azure ad. You can set the scope named like 'AA_Custom_Impression', and then you can add this delegate api permission to the client azure ad app, then you that app to generate an access token with the scope of 'AA_Custom_Impression'. After appending the Bearer token in calling request, it will be filtered by backend code.
I don't know about python, so I can just recommend you this sample, you may try it, it's provided by microsoft.
I've solved the similar issue. I don't found how to directly validate access token, but you can just call graph API on backend with token you've got on client side with MSAL.
Node.js example:
class Microsoft {
get baseUrl() {
return 'https://graph.microsoft.com/v1.0'
}
async getUserProfile(accessToken) {
const response = await got(`${this.baseUrl}/me`, {
headers: {
'x-li-format': 'json',
Authorization: `Bearer ${accessToken}`,
},
json: true,
})
return response.body
}
// `acessToken` - passed from client
async authorize(accessToken) {
try {
const userProfile = await this.getUserProfile(accessToken)
const email = userProfile.userPrincipalName
// Note: not every MS account has email, so additional validation may be required
const user = await db.users.findOne({ email })
if (user) {
// login logic
} else {
// create user logic
}
} catch (error) {
// If request to graph API fails we know that token wrong or not enough permissions. `error` object may be additionally parsed to get relevant error message. See https://learn.microsoft.com/en-us/graph/errors
throw new Error('401 (Unauthorized)')
}
}
}
Yes we can validate the Azure AD Bearer token.
You can fellow up below link,
https://github.com/odwyersoftware/azure-ad-verify-token
https://pypi.org/project/azure-ad-verify-token/
We can use this for both Django and flask.
You can directly install using pip
but I'm not sure in Django. If Django install working failed then try to copy paste the code from GitHub
Validation steps this library makes:
1. Accepts an Azure AD B2C JWT.
Bearer token
2. Extracts `kid` from unverified headers.
kid from **Bearer token**
3. Finds `kid` within Azure JWKS.
KID from list of kid from this link `https://login.microsoftonline.com/{tenantid}/discovery/v2.0/keys`
4. Obtains RSA key from JWK.
5. Calls `jwt.decode` with necessary parameters, which inturn validates:
- Signature
- Expiration
- Audience
- Issuer
- Key
- Algorithm
I have a mobile client (Android), an API (WebAPI .net Core 3.1) and an IdentityServer4.
I login with my client to IdentityServer and get back an Identity token and a Access Token.
I use the access token to access the API... all good so far.
Now in the API, on the first time only, I need to update a table with the users first and last name plus some other identity stuff, but these claims are not available in the API (because this info is not available in the access token)
My question is, how do I go about getting the user claims from my API code?
I could pass the various claims as string parameters to the API call, but the mobile client is unable to see if this update has taken place, so I would have to pass this information every time I call the API, which is very wasteful as it is only required the first time. I also prefer this to be done without exposing this in an endpoint in my API.
Can anyone suggest how I can get the user (profile) claims of the user with in the API?
Update:
Now I have found an endpoint in IdentityServer that could help, called "userinfo_endpoint" but this needs an Access Token. Can I somehow re-use the Access Token that is used to access the API within my API code?
Yes, you can use the same access token to contact the user info endpoint to get additional user information.
If you were to use JavaScript to access it, it could look like:
//Make a AJAX-call to the user endpoint
$.ajax({
url: "#OpenIDSettings.userinfo_endpoint",
type: 'GET',
headers: { "Authorization": 'Bearer ' + params.access_token }
}).done(function (data) {
$("#userinfo").text(JSON.stringify(data, null, 2));
});
You just set the Authorization header.
You can also add user claims to the access token by providing UserClaims in your ApiScopes or ApiResource definitions, like:
new ApiScope()
{
Name = "shop.admin",
DisplayName = "You can administrate the e-shop systems",
Description = "Full admin access to the e-shop system.",
Emphasize = false,
Enabled = true,
Required = false,
UserClaims = new List<string>
{
//Custom user claims that should be provided when requesting access to this API.
//These claims will be added to the access token, not the ID-token!
"seniority",
}
}
I have created an app that simply uses a JWT sent by the server upon correct login credentials, and authorizes against any /api route on my backend Express.js server.
AngularJS, on the other hand, took this token, stored it in session storage, and used an auth interceptor every go around to send the token back to the server.
I've more recently come to understand how dangerous this practice is.
I understand the transfer method of tokens back and forth, in this scenario. However, would someone be so kind as to explain, at a high level, the method that takes place when you want to store that JWT inside a secure, HTTP only cookie that the client side Javascript cannot read?
For example: upon credential success
cookie is created on server,
create a JWT at the same time as the cookie
store the JWT in a cookie property called token etc..
I'm trying to gain a mental model here of how it works. If my understanding is correct, doing it this way wouldn't require an auth interceptor anymore because upon correct credential login, the server would do all of the transferring of the token inside the cookie.
Dealing with cookies has their fair share of subtleties, but at a high level a cookie is a piece of data that your web server can set, that will be then stored by the user's web browser and sent back to the server on any future requests that browser makes to the same server as long as the cookie is valid and applicable to the request being made.
(this is why you'll no longer need to use the Angular interceptors, because it's the browser itself that ensures the cookie is sent)
Besides some specials flag options, like the HTTP only, at a higher level you can set cookies to be associated with a given domain and path. For example, your server could set a cookie in such way that it would only be later sent by the browser to requests made under the /api path.
To sum it up, cookies are a state management mechanism for HTTP, see the associated RFC 2617 for more details.
In contrast, a JWT is just some data that has a well-know representation and follows some conventions. More specifically, a JWT is composed of a header, payload and signature sections and is generally advised to keep the size of the payload small for most of the JWT use cases. See Get Started with JSON Web Tokens for more details.
If you go through the previous article you'll notice that the final representation of a JWT is three Base64url encoded strings separated by dots. This is specially of interest because it means a JWT is well-suited to be used within HTTP, including as the value of a cookie.
One thing to have in mind is that by the specification you are only guaranteed that a browser will support a cookie up to 4096 bytes per cookie (as measured by the sum of the length of the cookie's name, value, and attributes). Unless you're storing way to much data in the token you should not have an issue, but it's always something to consider. Yes, you can also break a JWT token into multiple cookies, but things start to get more complex.
Additionally, cookies have their notion of expiration, so have that in mind also because the JWT itself, when used within the scope of authentication will also have thei own notion of expiration.
Finally, I just want to address some of your concerns about storing the JWT in localStorage/sessionStorage. You're correct that if you do it you have to understand its implication, for example, any Javascript code within the domain for which the storage is associated will be able to read the token. However, HTTP only cookies are also not a silver-bullet. I would give the following article a read: Cookies vs Tokens: The Definitive Guide.
It focuses on the differences between the traditional session identifier cookies vs the token-based (JWT) authentication systems, the section named Where to Store Tokens? warrants a read as it tackles the security related aspects of storage.
A summary for the TL:DR folks:
Two of the most common attack vectors facing websites are Cross Site
Scripting (XSS) and Cross Site Request Forgery (XSRF or CSRF). Cross Site Scripting) attacks occur when an outside entity is able to execute code within your website or app. (...)
If an attacker can execute code on your domain, your JWT tokens (in local storage) are vulnerable. (...)
Cross Site Request Forgery attacks are not an issue if you are using JWT with local storage. On the other hand, if your use case requires you to store the JWT in a cookie, you will need to protect against XSRF.
(emphasis is mine)
Basically, I save access_token(jwt) in a refresh token object stored in the database when the user logs in. see an example of the saved object below;
const newToken = new RefreshToken({
issuedUtc: moment().unix(), /* Current unix date & time */
expiresUtc: moment().add(4, "days").unix(), /* Current unix date&time + 4 days */
token: refreshToken, /* Generate random token */
user: data.id, /* user id */
/* Signing the access Token */
access_token: jwt.sign(
{ sub: data.id, user: userWithoutHash },
Config.secret,
{
issuer: "http://localhost:3000",
expiresIn: "30m", // Expires in 30 minutes
}
),
});
The generated and saved rand token is then sent as httpOnly cookie to the browser;
res.cookie("refreshToken", newToken.token, {
httpOnly: true,
sameSite: "strict",
});
Since the browser sends the cookie for every request all that is left is to use middleware on protected routes, retrieve the token from the cookie, verify if it is exists by looking for it in the database, check if it has not expired, try to verify the access token saved in the database for that refresh token, if it is expired then sign new jwt and update the refresh token in the database then allow the user to proceed to protected route, if it is valid simply allow the user to proceed to protected route. If the refresh token has expired, redirect the user to the login page, and lastly if no refresh token is received also redirect the user to the login page.
var cookie = await getcookie(req); // get the cookie as js object using my custom helper function
/* Check if refresh token was received */
if (cookie.refreshToken) {
/* Check find the refresh token object in the database */
var refreshToken = await RefreshToken.findOne({
token: cookie.refreshToken,
});
/* Check if the refresh token is still valid using expiry date */
if (moment.unix(refreshToken.expiresIn) > moment.now()) {
/* If the condition is fulfilled try to verify the access token using jwt */
jwt.verify(refreshToken.access_token, Config.secret, async (err, result) => {
/* in callback check for error */
if (err) {
/* If error this means the access_token is expired, so find and update the user's refresh token with a newly signed access token */
await RefreshToken.findByIdAndUpdate(refreshToken.id, {
access_token: jwt.sign(
{ sub: result.id, user: result.user },
Config.secret,
{
issuer: "http://localhost:3000",
expiresIn: "30m", // Expires in 30 minutes
}
),
});
/* Proceed to save the user in a local variable then call next */
res.locals.user = result.user;
return next();
}
/* If no error proceed by saving the user in a local variable then call next */
res.locals.user = result.user;
return next();
});
} else {
/* If the refresh token is expired, then redirect to log in */
return res.status(401).redirect('/login');
}
} else {
/* If no refresh token is provided, then redirect to log in */
return res.status(401).redirect('/login');
}
This is something I came up with myself so I can't say it is full proof but since httpOnly cookie cannot be accessed in the DOM, running malicious script in the DOM can't access the refresh token, and even if the refresh token somehow falls in the hand of bad guys then it will be useless because it does not hold any information at all until it gets to the server. So as long the right cors header is set on the server it is highly unlikely that any information can be leaked using the refresh token.
I have a project split up in backend and frontend, the backend (API rest) is built in Laravel 5 and frontend in AngularJS. Both project are independent and they are supposed to be hosted on different servers.
In the first request I obtain the CSRF token from Laravel with this code:
var xhReq = new XMLHttpRequest();
xhReq.open("GET", "http://laravel.local/api/token", false);
xhReq.send(null);
angular.module('mytodoApp').constant('CSRF_TOKEN',xhReq.responseText);
So the CSRF_TOKEN is sent each time that I make a request to API, like this:
$scope.deleteTodo = function(index) {
$scope.loading = true;
var todo = $scope.tours[index];
$http.defaults.headers.common['XSRF-TOKEN'] = CSRF_TOKEN;
console.log($http.defaults.headers.common['XSRF-TOKEN']);
$http.delete('http://laravel.local/api/deleteTodo/' + todo.id, {headers : {'XSRF-TOKEN': CSRF_TOKEN}})
.success(function() {
$scope.todos.splice(index, 1);
$scope.loading = false;
});
The API always return:
TokenMismatchException in compiled.php line 2440:
Is it right that Laravel changes the CSRF Token with every request from Angular? On every request, Laravel creates a new file on storage/framework/sessions. Do you recommend any other solution to validate that requests to API come from a safe origin?
In token-based authentication, cookies and sessions will not be used. A token will be used for authenticating a user for each request to the server.
It will use the following flow of control:
The user provides a username and password in the login form and clicks Log In.
After a request is made, validate the user on the backend by querying in the database. If the request is valid, create a token by using the user information fetched from the database, and then return that information in the response header so that we can store the token browser in local storage.
Provide token information in every request header for accessing restricted endpoints in the applications.
4.request header information is valid, let the user access the specified end point, and respond with JSON or XML.
This Can be Achieved by Jwt (Json web Token).got this information from This link.
So, what is this JWT?
JWT
JWT stands for JSON Web Token and is a token format used in authorization headers. This token helps you to design communication between two systems in a secure way. Let's rephrase JWT as the "bearer token" for the purposes of this tutorial. A bearer token consists of three parts: header, payload, and signature.
The header is the part of the token that keeps the token type and encryption method, which is also encrypted with base-64
The payload includes the information. You can put any kind of data like user info, product info and so on, all of which is stored with base-64 encryption.
The signature consists of combinations of the header, payload, and secret key. The secret key must be kept securely on the server-side.
The tutorial with example can be Found here Token-Based Authentication With AngularJS & NodeJS.
Hope that this will solve your problem,All the Best!!