How to do JWT authentication with react? - javascript

I'm not sure how to properly do JWT authentication on the front end, I didn't have almost any problems on the back end though.
with Javascript I didn't get too far past this snippet
as I had to redo stuff many times and it didn't work anyway
const submit = async (e) => {
e.preventDefault();
await axios('http://localhost:8080/api/login', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
credentials: 'include',
data:new URLSearchParams(`username=${username}&password=${password}`)
});
}
Which basically just displays the access&refresh tokens in the network info of the page.
how do I actually identify the user on a website from there on and actually be able to use the user's own data, such as files for example?
I need to just be able to identify the user, all their data, such profile-info, photos etc...
and the user to only be able to access its own data

You already have what you need. the access&refresh tokens are the ones needed for routes that require authorization. The token has the user details which identifies them to the back end who is making requests. after login you save the token on the browser and as long as the token is valid the user is logged in. The token are sent along with requests that need authorization to access as a header like this
let say to get user full profile we need to have been logged in ->
axios(' api/user/profile ', { method: 'POST', headers: {'Content-Type': 'application/json,'Authorization':'Bearer' + localStorage.getItem('user-token')}, });.

JWTs are used to encode what actions user is allowed to take and which pages to see. For example you could encode their role inside the token to indicate what they are allowed to access. In order to retrieve their data such as profile info, photos etc. you have to either send additional information, probably the user id or you have to encode the id field inside the token which is not there by default.

Related

Next Js - How to retrieve data from the api requiring authorization (via axios)?

enter image description here
user = Cookies.get("user")
I can't get access to this road. I used cookies to store user data including the token.
Please I need Help
I think your using Strapi CMS and request method for authenticated roots must be POST not GET here the example :
const {data ,status} = await
axios.post('https://localhost:1337/posts',{
headers : {
Authorization: `Bearer ${token}`
}
});
if(status === 200){
setData(data)
}else{
console.log('eeror')
}
make sure to mark the find method from strapi admin otherwise you will get 403 error in strapi user has jwt not token so make sure the token is the jwt which come from strpai when user login!

php jwt login storage

I'm building a web app with CodeIgniter 4 where I implemented a REST API for register and login and a profile page just a test my login.
I have a login form that sends a javascript fetch post request to my api and I receives a jwt token. This is working.
Now I am in the situation where I think I did not understand the principle.
I want that the user stays logged in and doesn't need to re-login every time. The token expires after 12h.
And I want to use php (if possible) as the entire app runs on php.
Currently, I have a little javascript function to store my token:
const store = {};
store.setJWT = (data) => {
this.JWT = data;
};
But this is not secure against page reload.
Additionally I am creating a cookie with php, when the user logs in:
helper('cookie');
set_cookie([
'name' => 'login',
'value' => $token,
'expire' => $exp,
'httponly' => true,
'secure' => true,
]);
I am able to fetch data from the API using the cookie or the store object.
const token = getCookie('login');
const res = await fetch('/profile', {
headers: {
'Authorization': `Bearer ${token}` // or store.JWT
}
});
So.... what I want is:
The user goes to a protected url e.g. https://myapp.com/profile and if he is logged in, he has access. If not, he gets redirect to the login page.
Is using the cookie to store the jwt a good idea? Or did I completely misunderstood the idea of JWT and it is not used to be used for a login?
Additionally: I still don't know if biulding the login as an API was the best idea.
First of all there is nothing wrong with building "login with API". It is common practice. And JWT is perfectly suited for auth.
You sure can store JWT token inside a cookie, but it is a little bit wrong in my opinion. Usually JWT tokens are stored in the local storage on the client side. It will persist after page reload.
Set token in the local storage:
localStorage.setItem('token', token);
Get token from the local storage:
token = localStorage.getItem('token');
To better understand the conception of the JWT you can copy some token (without Bearer) and paste it in jwt.io. Basically JWT contain all the required information about the user and server can trust this information.
And when you set the cookie like this
set_cookie([
'name' => 'login',
'value' => $token,
'expire' => $exp,
'httponly' => true,
'secure' => true,
]);
It is an overkill. JWT possible already contains all this information and you can extract it from the token.

How to properly deal with JWT in headers

I'm trying to set up JWT in my project (NodeJs Express for the backend, and Javascript in frontend).
So in my backend, I send my token like this when the user logs in :
[...]
return res.send({ token, id: userId })
[...]
And in my frontend, I set the Bearer token in my header like this :
const data = {
email,
password
}
await instance.post('/signin', data)
.then((res) => {
instance.defaults.headers.common['authorization'] = `Bearer ${res.data.token}`
window.location = './account.html'
})
.catch((err) => console.log(err))
My questions are :
Did I do it correctly ?
When I'm redirected to the account.html page, how do I retrieve the token that was set while the log in was made ?
When you assign to instance.defaults.headers.common['authorization'] then subsequent requests made via Ajax using whatever library (I'm guessing Axios) that is from that page will include the authorization header.
Assigning a new value to window.location will trigger navigation and discard the instance object (with the assigned header value).
(As a rule of thumb, if you are going to assign a new value to window.location after making an Ajax request then you should probably replace the Ajax request with a regular form submission).
You could assign the data to somewhere where you can read it in the account.html page (such as local storage).
That won't be available to the server for the request for account.html itself though.
You could assign the data to a cookie. Then it would be available for the request for account.html, but in a cookie and not an authorisation header.
Note that using JWTs in authorization headers is something generally done in Single Page Applications and not traditional multi-page websites.

Teams proactive messages and Graph API token

I'm trying to build a bot that sends proactive user recommendations regularly. They look similar to this one:
I have it all working in terms of user data coming from the backend, but I also want to add some additional things coming from the Graph API - one of which is the profile picture.
I've setup an Azure Bot Channel, got the Graph auth sample running, but I still can't figure how to mix the proactive messages with the OAuthPrompt dialog.
If I make the user sign in upon app registration, can I reliably get the graph token and use it in my proactive message handler? Note that these messages are going to be sent on a weekly basis. I'm afraid that the token is going to expire.
Has anyone done something similar?
If you just need the bot to make a call to Graph and retrieve user data, you can use Application Permissions to do this without having the user log in. First, you will need to enable the permissions in Azure Active Directory>App registrations>API permissions. The particular ones you need here is User.Read.All (or User.ReadWrite.All if you might want it to have write access for other use cases). There are also separate permissions for Group and Contact if you need that.
For the bot, you first need to get the token. There's a whole reference article here (which includes the app permissions as described above). The client ID and secret are the values for your application. So in javascript I do something like
var identityUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
var formData = `client_id=${clientId}&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default&client_secret=${clientSecret}&grant_type=client_credentials`
try {
var idResponse = await request({
url: identityUrl,
method: 'POST',
headers: {'Content-Type': 'application/x-www-urlencoded;charset=UTF-8'},
form: formData
});
var token = JSON.parse(idResponse).access_token;
} catch (err) {
await step.context.sendActivity(`Sorry, something went wrong. Please try again later.`);
console.log(err.message);
return step.endDialog();
}
I've got a lot going on where I'm making an actual call to graph, but my http call looks like this:
var userResponse = await request({
url: usersUrl + queryString,
method: 'GET',
headers: {'Authorization':`Bearer ${token}`, 'ConsistencyLevel':'eventual'}
});
userResponse = JSON.parse(userResponse);
Now in your case you're calling the Get Photo endpoint, which I haven't done, but should be basically the same as the above. Here is a link for the Get photo documentation. So now, you bot should be able to authenticate and grab the picture before sending the proactive message, without any need for the user to ever give any credentials.

Get Auth0 access_token from existing id_token

I'm using auth0 to authenticate my logins to my Single Page App (built on React). I'm mostly using the base API calls (listed here).
The process I'm using is:
get username/email and password when the user enters them on my app's login page
Send a POST request to /oauth/ro with those values - here is that code:
export const login = (params, err) => {
if (err) return err
const {email, password} = params
const {AUTH0_CLIENT_ID, AUTH0_DOMAIN} = process.env
return fetch(`${AUTH0_DOMAIN}/oauth/ro`, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
'client_id': AUTH0_CLIENT_ID,
'username': email,
'password': password,
'connection': 'Username-Password-Authentication',
'grant_type': 'password',
'scope': 'openid',
'device': '',
'id_token': ''
})
})
.then(response => response.json())
.then(json => {
const {id_token, access_token} = json
setCookieValue('id_token', id_token) // utility function I wrote
return getProfile(access_token)
.then(data => {
const {user_id, email: emailAddress, picture, name} = data
return {id_token, user_id, emailAddress, picture, name}
})
})
.catch(error => console.log(`ERROR: ${error}`))
}
This is all sent through Redux and the user is logged in (assuming the username/password was correct).
However, I'm trying to figure out how to persist the login when refreshing the page/coming back to the app. I'm saving the id_token (which is a JWT) in the browser's cookies and can fetch this when the app renders server-side. I can decode the JWT and get the payload (sub is the user ID from auth0). However, to get the profile data I need the access_token which Auth0 provides when using the /oauth/ro POST request. Obviously, if the JWT token has expired then it will just reject it and keep the user logged out.
Here is my code to decode the JWT (happens on app render):
const ID_TOKEN = req.cookies.id_token || false
if (ID_TOKEN) {
verifyJwt(ID_TOKEN, (err, decoded) => {
if (err) { console.log(`JWT Verification error: ${err}`) }
else {
const {sub} = decoded
getProfile(sub).then(data => store.dispatch(fetchUserDetails(data))) // fails as `sub` (the user id) is not the `access_token` which it requires
}
})
}
I have tried using the /oauth/ro call again, but this time specifying "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer" and using the id_token retrieved from the cookies, and specifying a device. However, when I do this call, I get this error from Auth0:
{
"error": "invalid_request",
"error_description": "there is not an associated public key for specified client_id/user_id/device"
}
So my question is, what API call do I need to make to get the access_token from the id_token JWT?
Also, as a bonus - when I do the POST request to login, the password is being transfered over plaintext. How would I encrypt this when sending to auth0 so they can decrypt it back? I assume it involves using the client_secret which auth0 provide but I'm not sure how to go about doing that.
The ability to refresh a token programmatically without any type of user interaction is accomplished through the use of refresh tokens. However, this is not applicable for browser-based applications because refresh tokens are long-lived credentials and the storage characteristics for browsers would place them at a too bigger risk of being leaked.
If you want to continue to use the resource owner password credentials grant you can choose to ask the user to input the credentials again when the tokens expire. As an alternative, upon authentication you can obtain the required user information and initiate an application specific session. This could be achieved by having your server-side logic create an application specific session identifier or JWT.
You can also stop using the resource owner password credentials grant and redirect the user to an Auth0 authentication page that besides returning the tokens to your application would also maintain an authenticated session for the user, meaning that when the tokens expired and your application redirected again to Auth0, the user might not need to manual reenter credentials because the Auth0 session is still valid.
In relation to the password being sent in plaintext; the resource owner endpoint relies on HTTPS so the data is encrypted at the protocol level. You must also use HTTPS within your own application for any type of communication that includes user credentials of any kind.
Also note that you can control what's returned within the ID token through the use of scopes, depending on the amount of information in question you might not even need to make additional calls to get the user profiles if you signal that you want that information to be contained within the ID token itself.

Categories