Has been almost a year that I switch to Auth0 in order to manage my customer's access to the dashboard of my application. Nowadays I need to implement access for a RESTFULL API.
If I follow the instructions in order to secure the NodeJS app using JWT it works like a charm. The issue is that I am not properly sure on the implementation for the end user in order to get the token needed for access this API.
I thought of creating the tokens on the dashboard or just use a server side implementation for the login/authentication. I did the last using the access to my own database before and worker amazingly. My issue is that I am not completely sure on how to do it for the end user using Auth0.
Anyone implemented a RESTfull API that has login using Auth0 before in order to get the JWT token ? Would my great to hear your thoughts.
The solution was to use a different approach.
There is an Auth0 endpoint that uses the user and password for the user in order to login with the service. This way I can get the id of the authenticated user and a JWT token that I can use to validate future requests.
https://auth0.com/docs/api/authentication#resource-owner-password
This flow should only be used from highly trusted applications that cannot do redirects. If you can use redirect-based flows from your apps we recommend using the Authorization Code Grant instead.
router.post('/login', function (req, res, next) {
var options = {
method: 'POST',
url: process.env.AUTH0_URL_OAUTH,
headers: {
'Cache-Control': 'no-cache',
'Content-Type': 'application/json'
},
body: {
grant_type: 'password',
username: req.body.username,
password: req.body.password,
audience: process.env.AUTH0_AUDIENCE,
scope: process.env.AUTH0_SCOPE,
client_id: process.env.AUTH0_CLIENT_ID,
client_secret: process.env.AUTH0_CLIENT_SECRET
},
json: true
};
request(options, function (error, response, body) {
if (error) {
res.sendStatus(500); //We could not connect to the service
} else {
if (body.error) {
console.log(body);
res.status(400);
res.send({
error: body.error_description // There was an error with the user or password
});
} else {
console.log(body);
/**
* Everything went well. We return the JWT
*/
res.send({
access_token: body.access_token,
expires_in: body.expires_in,
token_type: body.token_type
});
}
};
Related
Our app requires Azure b2c authentication when login in. I automated the login by simply typing the username and password. In addition I had to add "chromeWebSecurity": false to avoid cross-origin issue.
I think this is the incorrect way of login in. While I was searching for a solution I came across some articles regarding this. Apparently we could user Cy.request() and get the response and then access the app.
However, I was unable to implement this.
Has anyone implemented the Azure b2c login automation with cypress ? if so can someone explain how this needs to be done ?
Our app uses access bearer tokens.
It first sends authentication requests
GET https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/oauth2/v2.0/authorize
Then gets the token
POST https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/oauth2/v2.0/token
Thanks
I'm not too familiar with Azure b2c authentication, but looks like the general pattern is given here How to use Cypress to test your SharePoint solution with an Azure AD-secured API
The bulk of the code is this
Cypress.Commands.add("visitWithAdal", (pageUrl) => {
const config = {
username: process.env.CI ? Cypress.env('USERNAME') : Cypress.env('username'),
password: process.env.CI ? Cypress.env('PASSWORD') : Cypress.env('password'),
tenant: process.env.CI ? Cypress.env('TENANT') : Cypress.env('tenant'),
clientId: process.env.CI ? Cypress.env('CLIENTID') : Cypress.env('clientid'),
clientSecret: process.env.CI ? Cypress.env('CLIENTSECRET') : Cypress.env('clientsecret'),
resource: process.env.CI ? Cypress.env('RESOURCE') : Cypress.env('resource')
};
// Fetch the access token for the Microsoft Graph
cy.request({
method: 'POST',
url: `https://login.microsoft.com/${config.tenant}/oauth2/token`,
header: {
'cache-control': 'no-cache',
'Content-Type': 'application/x-www-form-urlencoded'
},
form: true,
body: {
grant_type: 'password',
client_id: config.clientId,
client_secret: config.clientSecret,
resource: config.resource,
password: config.password,
username: config.username
}
}).then(response => {
if (response && response.status === 200 && response.body) {
const accessToken = response.body["access_token"];
const expires = response.body["expires_on"];
// Store the retrieved access token in the session storage
cy.window().then((crntWindow) => {
crntWindow.sessionStorage.setItem(`adal.token.keys`, `${config.resource}|`);
crntWindow.sessionStorage.setItem(`adal.expiration.key${config.resource}`, expires);
crntWindow.sessionStorage.setItem(`adal.access.token.key${config.resource}`, accessToken);
cy.visit(pageUrl);
});
}
});
});
I'm a beginner with Node and React, and web programming in general. I want to import user credentials from LinkedIn's API and for that I need to authenticate using OAuth2.
My approach so far is to make an API-call from the client side to the LinkedIn oauth API with the relevant parameters, including a redirect URI which leads to an API endpoint on my node server. When the user has been redirected and approved LinkedIn's authentication dialog box, they will be redirected to the node server with an access token.
My question is as follows: I now want to update the user in my database with their corresponding access token, but how do I know which user to update when I can't get any information about the client in my function that handles the last redirect and fetches the access token?
Here's my node function that handles the redirect from LinkedIn:
router.get('/redirect', (req, res) => {
// Handle cancel by user
if(req.query.error){
console.log(req.query.error_description)
return
}
// Extract variables
const code = req.query.code
const state = req.query.state
// Check that state matches
if (state !== testState) {
console.log("State doesnt match")
return
}
// Exchange Authorization Code for an Access Token
var options = {
method: 'POST',
url: 'https://www.linkedin.com/oauth/v2/accessToken',
form: {
client_id: 'theClientID',
client_secret: 'theClienSecret',
grant_type: 'authorization_code',
code: code,
redirect_uri: 'http://localhost:3000/api/linkedin/redirect'
},
headers:
{ 'cache-control': 'no-cache',
"content-type": "application/json",
'user-agent': 'node.js' },
json: true };
// make the actual request
request(options, (error, response, body) => {
if (error) {
res.status(500).json({
message: error
})
return
}
// Extract access token
const token = body.access_token;
// Here I want to save access token to DB with the corresponding
// user, but I don't know which user to update
})
// Redirect user to profile
res.writeHead(301, {
Location: 'http://localhost:3000/profile'
})
res.end()
})
I had a really hard time formulating this question but I hope that my message gets through.
I am currently learning Node.js and Express and wanted to build a simple app that queries the Spotify Web API without using an external library such as spotify-web-api-node. My code is available at https://pastebin.com/Jwe8sckJ
My "/callback" route looks like this;
app.get("/callback", function(req, res){
res.send("OK!")
var authCode = req.query.code
var options = { method: 'POST',
url: 'https://accounts.spotify.com/api/token',
headers: { 'content-type': 'application/x-www-form-urlencoded' },
form:
{ grant_type: 'authorization_code',
code: authCode,
redirect_uri: 'https://example.com/callback',
client_id: clientId,
client_secret: clientSecret } };
request(options, function (error, response, body) {
if (error) throw new Error(error);
var accessToken = JSON.parse(body).access_token;
var refreshToken = JSON.parse(body).refresh_token
console.log("Access Token: " + accessToken);
console.log("Refresh Token: " + refreshToken);
});
});
So far this is all working fine, I am able to get an Access Token and a Refresh Token. The part that I am confused about is how do I now use these tokens outside of the "/callback" route? Or maybe another way of asking would be how do I save the variables globally. If I do that, would it have any impact on new sessions to script?
The most common solution would be to store these tokens in a database, i.e. MongoDB or MySQL and retrieve them when needed. You'll probably want to use sessions to set the received tokens per client.
You could even save the tokens in the session, but please note that sessions are volatile.
If I got you right then you want to save access token for later use. You can declare global variable and can use it later.
global.spotify_access_token = '';
Declare this variable at the start of any file. Now you can use this variable to save access token in callback.like below :
spotify_access_token = JSON.parse(body).access_token;
Hope this will help you.
Thanks.
** Disclaimer -- I'm new to the world of oAuth and OpenIDConnect -- please be patient if I'm asking a stupid question here.
I want to create a SPA that will request data from an API. Both the SPA and API are hosted on the same nodejs server. I want anyone accessing data and/or the app to be authenticated with our AzureAD tenant on Office365.
Currently, I have the authentication piece working using passport-azure-ad.OIDCStrategy. However, in my app, I would also like to be able to access information from the Microsoft GRAPH api in the server side api code. However, the OIDC connection that I've already made does not seem to be enough to allow me access to the GRAPH api. It appears that maybe I need a jwt bearer token.
My question is, do I need to use the access token from the OIDC response to get a bearer token? If so, how do I go about this (on the server side -- nodejs)?
I tried viewing the example listed in passport-auth-ad for BearerStrategy v2 endpoint. What confuses me though is that it uses OIDCStrategy! Does that also return a bearer token? If so, am I already receiving everything I need in my first OIDCStrategy call?
Thanks for whatever help you can offer!
Update
https.request({
hostname: "graph.microsoft.com",
path: '/v1.0/me/messages',
port: 443,
method: 'GET',
headers: {Authorization: 'Bearer ' + req.user.token, Accept: "application/json"}
},(rs) => {
console.log("HTTPS Response Status: ", rs.statusCode);
console.log("HTTPS Response Headers: ", rs.headers)
rs.on('data', (d) => {
res.send(d)
})
}).end();
Error Message:
{
"error": {
"code": "InvalidAuthenticationToken",
"message": "Access token validation failure.", ...
I confirmed that the token is the same token that was passed as the id_token in the auth callback from Azure. Any thoughts?
Update 2
A few more code snippets to help in diagnosing where I may be going wrong.
Strategy Config
//Still test code so user management not fully implemented
passport.use("azure", new azureStrategy({
identityMetadata: 'https://login.microsoftonline.com/common/.well-known/openid-configuration',
clientID: "*********************",
responseType: 'code id_token',
issuer: "https://sts.windows.net/****************/",
responseMode: 'form_post',
redirectUrl: "https://localhost:5070/auth/azure/callback",
allowHttpForRedirectUrl: true,
clientSecret: "***************" ,
state: "************"
},
(iss, sub, profile, claims, accessToken, refreshToken, params, done) => {
process.nextTick(() => {
var user = usvc.findUserByAltId(profile.oid, "azure");
if(!user){
}
})
done(null, {id: profile.oid, name: profile.displayName, email: profile.upn, photoURL: "", token: params.id_token });
}));
Route Definitions
app.get("/auth/azure", azure.passport.authenticate(
'azure', {scope: ['Mail.Read','User.Read'], failureRedirect: '/'}))
app.post("/auth/azure/callback", azure.passport.authenticate(
"azure", {scope: ['Mail.Read','User.Read'], failureRedirect: "/error.html"}),
(req, res) => {res.redirect("/user")})
The OpenIDConnect work grand flow also will returns a JWT token for Authentication & Authorization. You can use the id_token in Authentication header for the resources. However, some operations in Graph APIs require an administrator permission.
You can try to run the following script in PowerShell to upgrade your Azure AD application's privilege.
Connect-MsolService
$ClientIdWebApp = '{your_AD_application_client_id}'
$webApp = Get-MsolServicePrincipal –AppPrincipalId $ClientIdWebApp
#use Add-MsolRoleMember to add it to "Company Administrator" role).
Add-MsolRoleMember -RoleName "Company Administrator" -RoleMemberType ServicePrincipal -RoleMemberObjectId $webApp.ObjectId
I am using https://github.com/AzureAD/passport-azure-ad to integrate a nodejs application with Azure AD.
I used the OIDCStrategy to login a user into my node.js application.
Here's my code:
(passport.use(new OIDCStrategy({
callbackURL: config.azureadCreds.callbackURL,
realm: config.azureadCreds.realm,
clientID: config.azureadCreds.clientID,
clientSecret: config.azureadCreds.clientSecret,
scope: "openid offline_access profile Directory.Read UserProfile.Read",
identityMetadata: config.azureadCreds.identityMetadata,
skipUserProfile: config.azureadCreds.skipUserProfile,
responseType: config.azureadCreds.responseType,
responseMode: config.azureadCreds.responseMode,
passReqToCallback: true
},
function (iss, sub, profile, claims, accessToken, refreshToken, params, done) {
request.get("https://graph.windows.net/<my-tenant-id>/me?api-version=1.5", {
'headers': {
'Authorization': "Bearer " + params["id_token"],
'Content-Type': 'application/json'
}
}, function(err, res, body){
if(err){
console.log("err: " + err);
}
else{
console.log("res: " + res);
}
done();
});
})));
However I am unable to get any response. I have tried a variety of configurations, but no luck.
As Passport build on Authorization Code Grant Flow, and to get user profiles via Graph APIs, we need to build the application on Service to Service Calls Using Client Credentials.
The access token gotten by Authorization Code cannot be simply used as authorization in Graph APIs.
So we need to build custom HTTP requests for the access token via client_credentials, then leverage this token to traverse azure Graph APIs.
Build request to access token:
Use access token to traverse azure Graph APIs: