I started using OAuth2 server with oidc in node js. Github link
My goal is simple, to access https://myserver/me which is UserInfo endpoint.
While trying to learn how to use the server I also used this guide enter link description here
Where I found that I could create token by sending request to endpoint /token.
Into the configuration I added this code(full server code is below):
{
client_id: 'test_oauth_app',
client_secret: 'super_secret',
grant_types: ['client_credentials'],
redirect_uris: [],
response_types: [],
}
In postman I was able to get my the access_token by this request
POST /token
Headers:
Content-Type: application/x-www-form-urlencoded
Authorization: Basic dGVzdF9vYXV0aF9hcHA6c3VwZXJfc2VjcmV0
Body:
grant_type=client_credentials&scopes=api1
I get this as a response:
{
"access_token": "zdjmZo7_BQSIl4iK9IMcBbKffxGO-wQ3jLzzQXTlyws",
"expires_in": 600,
"token_type": "Bearer"
}
When I checked the token by /token/introspection I found out that the token equals to jti.
So I think it actually returns token_id and by that I cannot access /me endpoint.
Here is the whole sample of server that I use:
const { Provider } = require('oidc-provider');
const configuration = {
features: {
introspection: { enabled: true },
clientCredentials: { enabled: true },
userinfo: { enabled: true },
jwtUserinfo: { enabled: true },
},
formats: {
AccessToken: 'jwt',
},
clients: [{
client_id: 'test_oauth_app',
client_secret: 'super_secret',
grant_types: ['client_credentials'],
redirect_uris: [],
response_types: []
}],
scopes: ['api1']
};
const oidc = new Provider('http://localhost:3000', configuration);
oidc.proxy = true
// express/nodejs style application callback (req, res, next) for use with express apps, see /examples/express.js
oidc.callback
// koa application for use with koa apps, see /examples/koa.js
oidc.app
// or just expose a server standalone, see /examples/standalone.js
const server = oidc.listen(3000, () => {
console.log('oidc-provider listening on port 3000, check https://localhost:3000/.well-known/openid-configuration');
});
The proxy is set to true because I have https set up on apache redirecting to this server.
I tried to change response_types, but than it required redirect_uri which I do not want to have in my scenario.
Here is the request I am trying to post it like so:
POST /me
Headers:
Content-Type: application/json
Authorization: Bearer zdjmZo7_BQSIl4iK9IMcBbKffxGO-wQ3jLzzQXTlyws
The response:
{
"error": "invalid_token",
"error_description": "invalid token provided"
}
Did anyone have a similar problem? I found almost the same problem here
but with no solution, unfortunately.
In case someone encounters the same problem. I was able to solve it.
I did not have enough information and I did not know what client_credentials grant type does.
It actually does not authorize the user, but rather some app. So you have no info about the user, hence you cannot get data about the user through the userinfo endpoint.
So if you want to get info about the user, you probably want to use grant type authorization_code.
I found a page where a lot of things is written pretty clearly, so if you are starting with
OAuth server you might want to give this a try.
https://oauth2.thephpleague.com/authorization-server/auth-code-grant/
Related
I tried to get the access token and refresh token in the /token endpoint. I got authorization code and I will pass the token endpoint but it's throwing grant request is invalid error. How to fix this issue
Configuration
const oidc = new Provider('http://localhost:3000', {
clients: [
{
// client_id: 'foo',
// redirect_uris: ['https://jwt.io'], // using jwt.io as redirect_uri to show the ID Token contents
// response_types: ['id_token'],
// grant_types: ['implicit'],
// token_endpoint_auth_method: 'none',
client_id: 'secret',
redirect_uris: ['http://localhost:3000/api/v1'], // using jwt.io as redirect_uri to show the ID Token contents
response_types: ['code'],
grant_types: ['authorization_code', 'refresh_token'],
token_endpoint_auth_method: 'none',
},
],
cookies: {
keys: 'secret key',
},
features: {
clientCredentials: {enable:true},
introspection: {enable:true}
},
pkce: {
required: true
},
token_endpoint_auth_method: "none",
});
Token API
How to fix this Issue. I tried many ways and referred many documentation but I could not get the solution
Your access token request is missing the PKCE code_verifier parameter.
you can start your provider process with DEBUG=oidc-provider:* to get more details for these errors.
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
});
}
};
bellow is my node js script to get google user details using accessToken
var accessToken = req.body.accessToken;
console.log(accessToken)
var google = require('googleapis');
//google api
var plus = google.plus('v1');
var OAuth2 = google.auth.OAuth2;
var oauth2Client = new OAuth2(
config.google.clientID,
config.google.clientSecret,
config.google.redirect_uri
);
oauth2Client.setCredentials({access_token: accessToken});
plus.people.get({
userId: 'me',
auth: oauth2Client
}, function (err, response) {
// handle err and response
if (err) {
reject(err)
} else {
console.log(response);
resolve(response)
}
});
need to get google login user details using accessToken. what is wrong in code?
The most likely cause is the user in question has not created a google+ profile. Here are a few more options.
I am not sure what information you are trying to get but the best way to get user info is to authecate a user using the profile scope then request the data directly of the user info endpoint
Request
GET /userinfo/v2/me HTTP/1.1
Host: www.googleapis.com
Content-length: 0
Authorization: Bearer uzG4XqnvucBFk3jylgxnbtNhoOt2wCc3QvUcjk7PZhJ5m6G7ibtlBwbAQntJjJtLsxZfoDjhbASpzLmcFnlZ9o4qoMd2fCV2eRb4O5XrKRAXC
Response
{
"family_name": "Lawton",
"name": "Linda Lawton",
"picture": "https://lh5.googleusercontent.com/-a1CWlFnA5xE/AAAAAAAAAAI/AAAAAAAAl1I/UcwPajZOuN4/photo.jpg",
"locale": "en",
"gender": "female",
"link": "https://plus.google.com/+LindaLawton",
"given_name": "Linda",
"id": "117200475532672775346"
}
You can also go though the google people api using the same profile scope
GET /v1/people/me HTTP/1.1
Host: people.googleapis.com
Content-length: 0
Authorization: Bearer NuzG4XqnvucBFk3jylgxnbtNhoOt2wCc3QvUcjk7PZhJ5m6G7ibtlBwbAQntJjJtLsxZfoDjhbASpzLmcFnlZ9o4qoMd2fCV2eRb4O5XrKRAXC
But this endpoint reads from Google+ so if the user has not filled anything out on their Google+ profile you wont see much data here.
You can use request module to get the user detail on your node server.
But Before requesting the user data, make sure you have authorized the API by giving it the desired scope. In your case, you need to give https://www.googleapis.com/auth/userinfo.profile in the scope.
When you receive your accessToken, use that token to call this google api
https://www.googleapis.com/oauth2/v1/userinfo
const request = require('request');
// use any api you want to call.
request({
url: 'https://www.googleapis.com/oauth2/v1/userinfo',
method: 'GET',
headers: {
'Authorization': `Bearer ${YourAccessToken}`,
'Accept': 'application/json'
}
}, function(err, response, _user) {
console.log('User Data', _user);
})
I hope this will solve your problem. If still there is some problem, you can test your Google APIs on OAuth 2.0 Playground
** 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
Working with a vendor with minimal API and new to OAuth2. Using npm packages oauth and request.
Creating a node module which would post data from a database to the server's API.
I've got the authorization token, but now I'm unsure how to send that with the data post request. The vendor server is responding with a simple Cannot POST /accounts/import.
So - how do I send the OAuth2 token back with the post data to the server?
var request = require('request'),
clcreds = require('../../creds/creds.js');
var OAuth = require('oauth');
var OAuth2 = OAuth.OAuth2;
var oauth2 = new OAuth2(clcreds.clientId, clcreds.clientSecret, 'https://URL.com/', null, 'token', null);
oauth2.getOAuthAccessToken('', {
'grant_type': 'client_credentials'
}, function(e, access_token, refresh_token, results) {
console.log(e, access_token, refresh_token, results);
request.post('https://URL.com/accounts/import', {
"accounts": [{
...
}]
}, function(e, r, body) {
//returns Cannot POST /accounts/import
})
})
Most resource servers support RFC 6750, 2.1. Authorization Request Header Field. If your server supports it, you can pass an access token to the server by adding Authorization header like below.
Authorization: Bearer YOUR-ACCESS-TOKEN
If your server does not support any means defined in RFC 6750, you have to ask the implementor of the server about how to pass an access token to the server.