I'm using the below guide to setup Oauth2 for my app.
https://learn.microsoft.com/en-us/graph/auth-v2-user
this is the /authorize URL get request, which is working fine:
GET https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=${clientId}&response_type=code&redirect_uri=${redirectUri}&response_mode=query&scope=offline_access%20user.read%20files.readwrite
then, i get the code from the redirectUri and POST to this URL:
POST https://login.microsoftonline.com/common/oauth2/v2.0/token
Which returns a access_token and a refresh_token.
However, whenever i need to refresh the tokens, Graph API only returns a new access_token.
I'm using axios and qs:
//get new tokens
const scope = "Files.ReadWrite";
const data = qs.stringify({
client_id: clientId,
client_secret: clientSecret,
grant_type: "refresh_token",
redirect_uri: redirectUri,
scope: scope,
refresh_token: oneDriveRefreshToken
});
const headers = {
"Content-Type": "application/x-www-form-urlencoded",
};
const response = await axios.post(tokenEndpoint, data, headers);
const json = response.data;
functions.logger.debug(json.access_token); //ok
functions.logger.debug(json.refresh_token); //undefined
As far as i understand, the authorization code flow along with "offline_access" scope should enable you to get a new refresh token when calling the /token endpoint
I noticed that the scope you defined in the code does not include offline_access, so it just returns you an access token with Files.ReadWrite permission. If you want to obtain an refresh token, please add offline_access to the scope.
Try to change the scope to: const scope = "Files.ReadWrite offline_access";.
Related
I am following this blog post on how to use the auth0-react library.
The post describes using the getAccessTokenSilently from the useAuth0 hook to get the access token which is used as the bearer token
const callSecureApi = async () => {
try {
const token = await getAccessTokenSilently();
const response = await fetch(`${apiUrl}/api/private-message`, {
headers: {
Authorization: `Bearer ${token}`,
},
});
const responseData = await response.json();
setMessage(responseData);
} catch (error) {
setMessage(error.message);
}
};
The issue I'm having is the token doesn't appear to be a JWT token - it looks like:
RJq7USOcszn7rpyI5iDjbYAKp9pK60Ap
Does anyone know why getAccessTokenSilently isn't returning a JWT token?
The same task took me a while as well, the docs are neither very clear nor up-to-date there I feel. Here is what you need to do:
For Auth0, you need both a Single Page Application under "Applications", and an API under "APIs"
Your React App gets wrapped with something like the below. Note the audience parameter from your API, this is the crucial part, without it, I also got the same small and useless access token you got!
<Auth0Provider
domain={AUTH0_DOMAIN} // taken from the SPA application in Auth0
clientId={AUTH0_CLIENT_ID} // taken from the SPA application in Auth0
authorizationParams={{
audience={AUTH0_AUDIENCE} // taken from your API in Auth0
redirectUri={window.location.origin}
}}
useRefreshTokens={true}
cacheLocation={"localstorage"}
>
Wherever you want to call your Backend (e.g. Spring Boot), you add the following to the component:
const {user, getAccessTokenSilently} = useAuth0();
...
// this will now be a JWT token, BUT ONLY if you specified the API audience in the Auth0Provider
// looks something like this:
/* eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IlFqZEVNMEU1TnpWRE0wTkRSRVEzUkVFMFFVSkJOamMwUmtGRE1qZzNPVGxCTURNMk56UTJSZyJ9.eyJpc3MiOiJodHRwczovL3JhcGlkbWluZXIuYXV0aDAuY29tLyIsGnN1YiI6ImF1dGgwfDUzNDc5YzMwNmU5Yjc4YTU3MzAwMDVjNSIsImF1ZCI6WyJodHRwczovL2VuZ2luZWVyaW5nLXRvb2xzLnJhcGlkbWluZXIuY29tL2FwaSIsImh0dHBzOi8vcmFwaWRtaW5lci5hdXRoMC5jb20vdXNlcmluZm8iXSwiaWF0IjoxNTk2ODM2MDMyLCJleHAiOjE1OTY5MjI0MzIsImF6cCI6IllldWhoS29JV1lTV2FYUmExNU1sNTFMZUExYkp4bjVlIiwic2NvcGUiOiJvcGVuaWQgcHJvZmlsZSBlbWFpbCJ9.zI74HHd1pCzz-xBQEDDKby9z_Ue9_AIz-r05_my1wvLQ0U94u9WrwWmSxd9BQ-2XOHKa1KgnaaxsX2aiSHil7a4YjMnrYo9f0jgMmlxcllqZJgeb0AhLQNfYQEr6nAKP_8PgN0D7QFjIxiFTpDndTcD_2nG9DEsxbraT7dDy0pf1KTGmhQnNyBuyReAEUFhlxd1LAd63ED14nCPmEehl5rLNUwClTRFQ5q4ERLjM8cX0GLLy5F-I7UjpDOBnrL_qMqfuHyuChxs-k0fHhhEfV8xE2nEV00cXcAp3zvpJ_Ox9U0OBaVUbf1vi9v1Wl6jaMZpgqRZ1bZcfDXWjoEBVlQ */
const accessToken = await getAccessTokenSilently();
Now you can pass the accessToken along via Authorization header:
headers: {
"Authorization": `bearer ${accessToken}`
}
Your BE can now validate the regular JWT token.
From Auth0 docs:
id_token: contains an ID Token and is present if the request parameter response_type included the value id_token, or the scope request parameter the value openid
From various Auth0 employees in the Auth0 forums:
How to get an access token in JWT format?
if you update the scope parameter to specify openid ... then you should receive a response containing an id_token parameter that would indeed be a signed JWT
OIDC Access Token (mine is opaque, but docs say it should be JWT)
In addition to specifying openid:
If you specify an audience, and the audience is a custom API you built, then you’ll get a JWT token
So, in the options object of getAccessTokenSilently, make sure:
the scope parameter includes openid
the audience parameter refers to your API
I am working on one authentication problem where i have to implement OAuth2.0 authentication for my React App. Is there any way that i can use that authentication with Axios Promise based library???
You will have to pass your Token in the header.
See below;
const instance = axios.create({
baseURL: 'http://localhost/api/',
headers: {'Authorization': 'basic '+ token}
});
instance.get('/path')
.then(response => {
return response.data;
})
OR
Set an Authorization cookie in the browser once you get your token.
The browser will always send the Authorization cookie in each request made to the server. You won't have to pass it through Axios get/post.
UPDATE:
In order to get the access token from the oAuth server, pass the client_id, client_secret, scope and grant_type as follows;
var axios = require("axios");
axios.request({
url: "/oauth/token",
method: "post",
baseURL: "http://sample.oauth.server.com/",
auth: {
username: "myUsername", // This is the client_id
password: "myPassword" // This is the client_secret
},
data: {
"grant_type": "client_credentials",
"scope": "public"
}
}).then(respose => {
console.log(respose);
});
I am assuming that you are using grant_type = "client_credentials", if not, then based on the grant_type you use, you will have to also change the request parameters accordingly.
I am having my application back-end implementation in Lumen which gives a JWT token every time a user logs in. The front end I am using Angular to save the token in the local storage and also I am adding it to all the headers in the subsequent requests.
To check the expiry of the token and refresh by creating a request I am using https://github.com/auth0/angular-jwt
I am adding the token refresh code in the config of the app but this method is never called when I make any other requests here is the code which I tried.
app.config(function Config($httpProvider, jwtInterceptorProvider) {
jwtInterceptorProvider.tokenGetter = function(jwtHelper, $http,$localStorage) {
if ($localStorage.currentUser) {
var token = $localStorage.currentUser.token;
if (jwtHelper.isTokenExpired(token)) {
return $http({
url: 'http://backend.mywebsite.com/token',
method: 'GET'
}).then(function(response) {
var token = response.token;
$localStorage.currentUser.token = token;
$http.defaults.headers.common.Authorization = 'Bearer ' + $localStorage.currentUser.token;
return token;
});
} else {
return token;
}
}
}
$httpProvider.interceptors.push('jwtInterceptor');
});
I would like to know how to configure this functionality so that whenever the token expires it is automatically refeshed and set in the http headers?
Points you should consider
You shouldn't change the default headers inside the tokenGetter function.
If your token is expired, you can't call the token endpoint.
You have two options, you can use Refresh tokens and make a post request to a delegation endpoint that makes use of the refresh tokens to obtain a new(not-expired) token.
OR
You can update the JWT with a delegation endpoint and request for a new access token just before the token expires. If the token has expired and there is no refresh_token, you can't really do anything.
A refresh token is a special kind of JWT that is used to authenticate a user without them needing to re-authenticate. It carries the information necessary to obtain a new access token.
In other words, whenever an access token is required to access a specific resource, a client may use a refresh token to get a new access token issued by the authentication server. Common use cases like yours include getting new access tokens after old ones have expired, or getting access to a new resource for the first time. Refresh tokens can also expire but are rather long-lived.
A sample code example for using a refresh token to obtain a new token after a token has expired can be found below:
angular.module('app', ['angular-jwt'])
.config(function Config($httpProvider, jwtInterceptorProvider) {
jwtInterceptorProvider.tokenGetter = function(jwtHelper, $http) {
var jwt = localStorage.getItem('JWT');
var refreshToken = localStorage.getItem('refresh_token');
if (jwtHelper.isTokenExpired(jwt)) {
// This is a promise of a JWT id_token
return $http({
url: '/delegation',
// This will not send the JWT for this call
skipAuthorization: true,
method: 'POST',
refresh_token : refreshToken
}).then(function(response) {
localStorage.setItem('JWT', response.data.jwt);
return jwt;
});
} else {
return jwt;
}
}
$httpProvider.interceptors.push('jwtInterceptor');
})
If you want more information about refresh tokens and how they work, you can check out this article.
how shall I send the token too access a view. Can I send it via POST or does it has to be via the header?
How can I send the token via header if that is necessary?
you have to send the token in the Authorization header. The token should be JWT <token>, as per documented in django jwt.
Here is the Angularjs based function I have written to show how to sign up, the code is very basic just for understanding you can write a separate service or factory, but for the sake of explaining this seems good.
$scope.registerUser = function(){
var postDict = $scope.user;
$http.post('http://127.0.0.1:8000/api/v1'+'/accounts/', postDict).success(function(data){
$scope.userRegistered = data;
var authData = {
username: data.username,
password: data.password
};
$http.post('http://127.0.0.1:8000/api-token-auth/', authData).success(function(data){
var token = data.token;
$http({
method : 'POST',
url : 'http://127.0.0.1:8000/api/v1/auth/login/',
data : authData, // pass in data as strings
headers : { "Content-Type": "application/json", "Authorization": "JWT "+data.token } // set the headers so angular passing info as form data (not request payload)
})
.success(function(data){
console.log(data);
var userdata = { "username": data.username, "first_name": data.first_name , "token": token , "last_name": data.last_name , "email": data.email};
$window.localStorage.setItem('userdata', JSON.stringify(userdata));
$state.go('app.dashboard');
});
});
});
}
now here we have obtained the token and in the headers property of the $http.post method of the angularjs, we have used this token for login.
This is how you can use Django JWT in Angularjs , also have a look at the django jwt documentation
You have to send it through a header named Authorization with the value: Token your-token-value.
In AngularJS you can do this through the $httpProvider in the configuration of your module, for instance:
angular.module('mymodule', []).config(function($httpProvider) {
$httpProvider.defaults.headers.common['Authorization'] = 'Token your-token-value';
});
After you do that, every request made with $http will have this header.
I am new to angular and am from .net framework. I need to post a angular request to .net service, where it expects two custom headers from the client.
angular post command:
var request = $http(
{
url: "http://localhost:53585/api/myService/Validate",
method: "POST",
data: JSON.stringify(payload),
headers: { 'first_token': sessionService.first_token, 'second_token': sessionService.second_token }
});
But in the service side, I can see only first_token in the request header and not the second token. What I am missing here?
Issue is with my service. I figured out and restarted the IIS and then service was able to read both the headers token
I found this method in a forum, it works.
return this.http.post<any>('https://yourendpoint', { username, password }, { headers: new HttpHeaders().set('Authorizaion', 'your token')})
.pipe(map(user => {
// login successful if there's a jwt token in the response
if (user && user.token) {
// sto`enter code here`re user details and jwt token in local storage to keep user logged in between page refreshes
localStorage.setItem('currentUser', JSON.stringify(user));
}
console.log(user);
return user;