Invalid token generating from service-principal and secret based login method - azure

Configured service principal and trying to use the token received from the method to hit customer insights API.
https://api.ci.ai.dynamics.com/v1/instances/{instanceId}/profilestore/stateinfo
Above API requires bearer token as header for authorization.
Token receiving from auth response is invalid and not accepting by Customer Insights API.
msRestNodeAuth.loginWithServicePrincipalSecretWithAuthResponse(clientId, secret,
tenantId).then((authres) => {
console.dir(authres, { depth: null })
}).catch((err) => {
console.log(err);
});
Also, tried the another method of getting access token using this endpoint
Still the token we are receiving are not getting accepted by customer insights.
'https://login.microsoftonline.com/'tenantid'/oauth2/v2.0/token';

Try to follow this article: https://learn.microsoft.com/en-us/dynamics365/customer-insights/audience-insights/apis
If you request the token with client credentials flow, it's need to add the application permission.
You could test in Postman using the scope(resource) https://azurecustomerinsights.com/.
POST https://login.windows.net/{tenant-id}/oauth2/token
Content-Type: application/x-www-form-urlencoded
client_id={}
&resource=https://azurecustomerinsights.com/
&client_secret={}
&grant_type=client_credentials
Server to Server via Client Credentials, see here:
var adal = require('adal-node').AuthenticationContext;
var authorityHostUrl = 'https://login.windows.net';
var tenant = 'myTenant';
var authorityUrl = authorityHostUrl + '/' + tenant;
var clientId = 'yourClientIdHere';
var clientSecret = 'yourAADIssuedClientSecretHere'
var resource = 'https://azurecustomerinsights.com/';
var context = new AuthenticationContext(authorityUrl);
context.acquireTokenWithClientCredentials(resource, clientId, clientSecret, function(err, tokenResponse) {
if (err) {
console.log('well that didn\'t work: ' + err.stack);
} else {
console.log(tokenResponse);
}
});

Related

How to generate Authorization Bearer token using client ID , tenant Id, Client secret of azure AD using NodeJs for calling REST API?

I am able to generate the token in Postman: using the following details.
tenant_id: 09872XXXXXXXXXXXXXXXXXX
grant_type: client_credentials
client_id: d7b7e-ighewiojwoei9-868767
client_secret:adat-XXXXXX-diupi825tfsq38XXXXX
resource: https://management.azure.com/
Want to achieve the same using NodeJs?
What you are using is the Azure AD client credential flow v1.0, to do this in node.js, you could use the ADAL for Node.js, change the resource to https://management.azure.com/, the applicationId is the client_id you used.
var AuthenticationContext = require('adal-node').AuthenticationContext;
var authorityHostUrl = 'https://login.windows.net';
var tenant = 'myTenant.onmicrosoft.com'; // AAD Tenant name.
var authorityUrl = authorityHostUrl + '/' + tenant;
var applicationId = 'yourApplicationIdHere'; // Application Id of app registered under AAD.
var clientSecret = 'yourAADIssuedClientSecretHere'; // Secret generated for app. Read this environment variable.
var resource = 'https://management.azure.com/'; // URI that identifies the resource for which the token is valid.
var context = new AuthenticationContext(authorityUrl);
context.acquireTokenWithClientCredentials(resource, applicationId, clientSecret, function(err, tokenResponse) {
if (err) {
console.log('well that didn\'t work: ' + err.stack);
} else {
console.log(tokenResponse);
}
});

Dynamics Business Central Azure AD ADAL Unauthorized

I developed a bare bones express app to test auth with Dynamics Business Central and ADAL in NodeJS. I'm getting the following 401 error. The authentication works as expected in Postman and I'm able to call the Dynamics REST endpoint in that context. In the JavaScript below I am using the same AAD tenant, client id, and client secret in Postman, but I'm not able to authenticate.
Compared the auth tokens given over Postman and in NodeJs using https://jwt.io/ and the only difference is the header values and the uti in the payload.
When I hit me getcompanies route I get the following error. I've listed my node package versions at the bottom of the post.
Error { error: { code: '401', message: 'Unauthorized' } }
Source code
var AuthenticationContext = require('adal-node').AuthenticationContext;
var crypto = require('crypto');
var express = require('express');
var request = require('request');
require('dotenv').config()
var clientId = process.env.CLIENT_ID;
var clientSecret = process.env.CLIENT_SECRET;
var authorityHostUrl = 'https://login.windows.net';
var azureAdTenant = 'grdegr.onmicrosoft.com';
var dynBusinessCentralCommonEndpoint = 'https://api.businesscentral.dynamics.com/v1.0/' + azureAdTenant + '/api/beta';
var bcRedirectUri = 'http://localhost:1337/getbctoken';
var dynBusinessCentralAuthUrl = authorityHostUrl + '/' +
azureAdTenant +
'/oauth2/authorize?response_type=code&client_id=' +
clientId +
'&redirect_uri=' +
bcRedirectUri +
'&state=<state>&resource=' +
'https://api.businesscentral.dynamics.com';
var app = express();
var port = 1337;
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
app.get('/bcauth', function(req, res) {
crypto.randomBytes(48, function(ex, buf) {
var bcToken = buf.toString('base64').replace(/\//g,'_').replace(/\+/g,'-');
res.cookie('bcauthstate', bcToken);
var dynBusinessCentralAuthUrlauthorizationUrl = dynBusinessCentralAuthUrl.replace('<state>', bcToken);
console.log('redirecting to auth url: ' + dynBusinessCentralAuthUrlauthorizationUrl);
res.redirect(dynBusinessCentralAuthUrlauthorizationUrl);
});
});
var bcAccessToken = '';
app.get('/getbctoken', function(req, res) {
var authorityUrl = authorityHostUrl + '/' + azureAdTenant;
var authenticationContext = new AuthenticationContext(authorityUrl);
console.log('getting bc auth context');
authenticationContext.acquireTokenWithAuthorizationCode(
req.query.code,
bcRedirectUri,
'https://api.businesscentral.dynamics.com/',
clientId,
clientSecret,
function(err, response) {
var message = '';
if (err) {
message = 'error: ' + err.message + '\n';
return res.send(message)
}
bcAccessToken = response.accessToken;
console.log('bc token\n' + bcAccessToken);
res.send('bc access token updated');
}
);
});
app.get('/getcompanies', (req, res) => {
var body = '';
var options = {
url: 'https://api.businesscentral.dynamics.com/v1.0/grdegr.onmicrosoft.com/api/beta/companies',
method: 'GET',
headers: {
Authorization: 'Bearer ' + bcAccessToken
},
json: JSON.stringify(body)
};
request(options, (err, response, body) => {
res.send(response || err);
if (response) {
console.log(body);
}
else {
console.log('response is null');
}
});
});
Node Packages
"devDependencies": {
"adal-node": "^0.1.28",
"request": "^2.87.0",
"webpack": "^4.12.0",
"webpack-cli": "^3.0.8"
},
"dependencies": {
"dotenv": "^6.1.0"
}
Some services are very strict when checking the aud (audience) value of an access token. Dynamics 365 Business Central expects the access token audience to be exactly https://api.businesscentral.dynamics.com. In your code, you are asking for, and getting an access token for https://api.businesscentral.dynamics.com/. That trailing slash at the end is what is making Dynamics 365 reject your access token invalid.
Change the token request to:
authenticationContext.acquireTokenWithAuthorizationCode(
req.query.code,
bcRedirectUri,
'https://api.businesscentral.dynamics.com', // <-- No trailing slash!
clientId,
clientSecret,
// ...
...and it should work.
However, there are two important things to note in your sample:
The pattern you are following is a bit strange, though it may be because you're in the early stages of development, or because it was just a minimal repro example for this question. You should not store an access token that way, because the next person who calls /getcompanies will be able to do so calling on behalf of the user who originally signed in, instead of signing in themselves. If you are looking to have users sign in with Azure AD and as part of that, call Dynamics 365 on behalf of the signed-in user, I suggest looking at passport-azure-ad.
Especially if you plan to have a system-wide account or access token, be very careful returning the original response to the end user. This is true even when developing, since it's very easy to overlook something like that when moving to production, and exposing what could be a very privileged access token to an unauthorized user.

Facebook accountkit gives error when exchanging tokens from nodeJS server

I have been integrating fb AccounKit with my ionic application (NodeJS server). Front-end part has been done and I'm able to send and receive OTPs and success status.
But while getting client token from authorization code, I keep getting ""Error verifying the token in the \'access_token\'"' error. I followed the same procedure mentioned in their official docs.
This is my code :
var me_endpoint_base_url = 'https://graph.accountkit.com/v1.0/me';
token_exchange_base_url='https://graph.accountkit.com/v1.0/access_token';
var params = {
grant_type: 'authorization_code',
code: request.body.code,
access_token: app_access_token
};
}
// exchange tokens
console.log(Querystring.stringify(params))
var token_exchange_url = token_exchange_base_url + '?' + Querystring.stringify(params);
Request.get({url: token_exchange_url, json: true}, function(err, resp, respBody) {
console.log(respBody);
var view = {
user_access_token: respBody.access_token,
expires_at: respBody.expires_at,
user_id: respBody.id,
};
var me_endpoint_url = me_endpoint_base_url + '?access_token=' + respBody.access_token;
Request.get({url: me_endpoint_url, json:true }, function(err, resp, respBody) {
console.log(respBody);
if (respBody.phone) {
view.method = "SMS"
view.identity = respBody.phone;
} else if (respBody.email) {
view.method = "Email"
view.identity = respBody.email.address;
}
});
});
Please help?
When making a sever-to-server call to exchange a code for a token, you need to supply your Account Kit App Secret in the access token you're sending.
So the access token should look like:
'AA|{app_id}|{app_secret}'
For ex.
var app_access_token = ['AA', app_id, app_secret].join('|');
You can find your app secret from the Account Kit dashboard. Go on "Account Kit" page under products section of your app on developers.facebook.com and click on the "Show" button next to the box for "Account Kit App Secret" to see your app secret.
Also remember that you should never include your app secret on any javascript code that runs on the client side. This secret is to be used only from your server-side node.js code and no one else should be able to see it

Firebase 3.0 Tokens : [Error: Invalid claim 'kid' in auth header.]

I'm trying to create JWT tokens in node.js for use with the REST api in firebase, but when I try to use them, I get the error "Error: Invalid claim 'kid' in auth header."
This is my code
http.createServer(function (req, res) {
var payload = {
uid: "bruh"
};
var token = jwt.sign(payload, sact["private_key"], {
algorithm: 'RS256',
issuer: sact["client_email"],
subject: sact["client_email"],
audience: 'https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit',
expiresIn: '3600s',
jwtid: sact["private_key_id"],
header: {
"kid": sact["private_key_id"]
}
});
res.writeHead(200);
res.end("It worked. (" + token + ")");
}).listen(port);
These are my requires
var http = require('http');
var jwt = require('jsonwebtoken');
Please use returnSecureToken: true, with correct Spellings
I hope it will solve the problem of Invalid claim 'kid' in the auth header.
This is an issue because you're generating a Firebase ID token, not an access token for the Firebase REST API.
To generate a REST API token I would use the legacy Firebase Token Generator library which still works perfectly well (but only generates REST tokens, not general purpose access tokens).
Note that your Firebase Database secret is now located under the gear icon in the top left of the console.
So I had this error and I've fixed it. Now here is the solution:
You'll need to retrieve the ID-token using an additional function. Here is the function you can use:
firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then(function(idToken) {
// Send token to your backend via HTTPS
// ...
}).catch(function(error) {
// Handle error
});
I implemented it somewhat like this:
//google OAuth login handler
const googleLoginHandler = () => {
const provider = new firebase.auth.GoogleAuthProvider();
firebase.auth()
.signInWithPopup(provider)
.then((result) => {
/** #type {firebase.auth.OAuthCredential} */
setgoogleAuthStatus(true)
// The signed-in user info.
const userId = result.user.uid;
const displayName = result.user.displayName;
const email = result.user.email;
//This is the function for getting the ID-Token
firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then((idToken) => {
// Send token to your backend via HTTPS
console.log(idToken)
}).catch((error) => {
// Handle error
console.log(error.message)
alert(error.message)
});
console.log(result)
}).catch((error) => {
console.log(error)
// Handle Errors here.
alert(error.message)
})
}
The id token you get by this method can be used to access the firebase real-time database and other firebase services.
check out these links for more details:
https://firebase.google.com/docs/auth/admin/verify-id-tokens#retrieve_id_tokens_on_clients
https://firebase.google.com/docs/database/rest/auth#firebase_id_tokens

Sign in to Nodejs web app, using Azure AD

I'm trying to figure out how you can authenticate users using Azure AD. In order to experiment, I tried the example from Microsoft, found at https://github.com/Azure-Samples/active-directory-node-webapp-openidconnect.
I've set up an Active Directory in Azure, and added a new application called test with add id uri: http://testmagnhalv.
Now, when I run the server, following the instructions in the readme, I get redirected to login.microsoftonline.com and promted to log in. But when I provide username/pw, I get redirected back to the login page again.
I suspect the problem is that I don't set the variables in the config.json correctly, but I'm having a hard time finding documentation for what values need to be set.
Anyone got any experience with this example?
At first you must to add your app to active directory then use ADAL (Active Directory Authentication Library) for nodeJS
npm install adal-node
prepare your app for authentication referencing azure AD App registration values.
var AuthenticationContext = require('adal-node').AuthenticationContext;
var clientId = 'yourClientIdHere';
var clientSecret = 'yourAADIssuedClientSecretHere'
var redirectUri = 'yourRedirectUriHere';
var authorityHostUrl = 'https://login.windows.net';
var tenant = 'myTenant';
var authorityUrl = authorityHostUrl + '/' + tenant;
var redirectUri = 'http://localhost:3000/getAToken';
var resource = '00000002-0000-0000-c000-000000000000';
var templateAuthzUrl = 'https://login.windows.net/' +
tenant +
'/oauth2/authorize?response_type=code&client_id=' +
clientId +
'&redirect_uri=' +
redirectUri + '
&state=<state>&resource=' +
resource;
Now you need to get authorized with the token.
function createAuthorizationUrl(state) {
return templateAuthzUrl.replace('<state>', state);
}
// Clients get redirected here in order to create an OAuth authorize url and redirect them to AAD.
// There they will authenticate and give their consent to allow this app access to
// some resource they own.
app.get('/auth', function(req, res) {
crypto.randomBytes(48, function(ex, buf) {
var token = buf.toString('base64').replace(/\//g,'_').replace(/\+/g,'-');
res.cookie('authstate', token);
var authorizationUrl = createAuthorizationUrl(token);
res.redirect(authorizationUrl);
});
});
And finally handle the auth redirection
// After consent is granted AAD redirects here. The ADAL library is invoked via the
// AuthenticationContext and retrieves an access token that can be used to access the
// user owned resource.
app.get('/getAToken', function(req, res) {
if (req.cookies.authstate !== req.query.state) {
res.send('error: state does not match');
}
var authenticationContext = new AuthenticationContext(authorityUrl);
authenticationContext.acquireTokenWithAuthorizationCode(
req.query.code,
redirectUri,
resource,
clientId,
clientSecret,
function(err, response) {
var errorMessage = '';
if (err) {
errorMessage = 'error: ' + err.message + '\n';
}
errorMessage += 'response: ' + JSON.stringify(response);
res.send(errorMessage);
}
);
});
You can find the full example, and more here in ADAL for nodeJS repository:
Windows Azure Active Directory Authentication Library (ADAL) for Node.js
This is a simple but full demo taken from GitHub ADAL repository
website-sample.js
'use strict';
var express = require('express');
var logger = require('connect-logger');
var cookieParser = require('cookie-parser');
var session = require('cookie-session');
var fs = require('fs');
var crypto = require('crypto');
var AuthenticationContext = require('adal-node').AuthenticationContext;
var app = express();
app.use(logger());
app.use(cookieParser('a deep secret'));
app.use(session({secret: '1234567890QWERTY'}));
app.get('/', function(req, res) {
res.redirect('login');
});
/*
* You can override the default account information by providing a JSON file
* with the same parameters as the sampleParameters variable below. Either
* through a command line argument, 'node sample.js parameters.json', or
* specifying in an environment variable.
* {
* "tenant" : "rrandallaad1.onmicrosoft.com",
* "authorityHostUrl" : "https://login.windows.net",
* "clientId" : "624ac9bd-4c1c-4686-aec8-e56a8991cfb3",
* "clientSecret" : "verySecret="
* }
*/
var parametersFile = process.argv[2] || process.env['ADAL_SAMPLE_PARAMETERS_FILE'];
var sampleParameters;
if (parametersFile) {
var jsonFile = fs.readFileSync(parametersFile);
if (jsonFile) {
sampleParameters = JSON.parse(jsonFile);
} else {
console.log('File not found, falling back to defaults: ' + parametersFile);
}
}
if (!parametersFile) {
sampleParameters = {
tenant : 'rrandallaad1.onmicrosoft.com',
authorityHostUrl : 'https://login.windows.net',
clientId : '624ac9bd-4c1c-4686-aec8-b56a8991cfb3',
username : 'frizzo#naturalcauses.com',
password : ''
};
}
var authorityUrl = sampleParameters.authorityHostUrl + '/' + sampleParameters.tenant;
var redirectUri = 'http://localhost:3000/getAToken';
var resource = '00000002-0000-0000-c000-000000000000';
var templateAuthzUrl = 'https://login.windows.net/' + sampleParameters.tenant + '/oauth2/authorize?response_type=code&client_id=<client_id>&redirect_uri=<redirect_uri>&state=<state>&resource=<resource>';
app.get('/', function(req, res) {
res.redirect('/login');
});
app.get('/login', function(req, res) {
console.log(req.cookies);
res.cookie('acookie', 'this is a cookie');
res.send('\
<head>\
<title>FooBar</title>\
</head>\
<body>\
Login\
</body>\
');
});
function createAuthorizationUrl(state) {
var authorizationUrl = templateAuthzUrl.replace('<client_id>', sampleParameters.clientId);
authorizationUrl = authorizationUrl.replace('<redirect_uri>',redirectUri);
authorizationUrl = authorizationUrl.replace('<state>', state);
authorizationUrl = authorizationUrl.replace('<resource>', resource);
return authorizationUrl;
}
// Clients get redirected here in order to create an OAuth authorize url and redirect them to AAD.
// There they will authenticate and give their consent to allow this app access to
// some resource they own.
app.get('/auth', function(req, res) {
crypto.randomBytes(48, function(ex, buf) {
var token = buf.toString('base64').replace(/\//g,'_').replace(/\+/g,'-');
res.cookie('authstate', token);
var authorizationUrl = createAuthorizationUrl(token);
res.redirect(authorizationUrl);
});
});
// After consent is granted AAD redirects here. The ADAL library is invoked via the
// AuthenticationContext and retrieves an access token that can be used to access the
// user owned resource.
app.get('/getAToken', function(req, res) {
if (req.cookies.authstate !== req.query.state) {
res.send('error: state does not match');
}
var authenticationContext = new AuthenticationContext(authorityUrl);
authenticationContext.acquireTokenWithAuthorizationCode(req.query.code, redirectUri, resource, sampleParameters.clientId, sampleParameters.clientSecret, function(err, response) {
var message = '';
if (err) {
message = 'error: ' + err.message + '\n';
}
message += 'response: ' + JSON.stringify(response);
if (err) {
res.send(message);
return;
}
// Later, if the access token is expired it can be refreshed.
authenticationContext.acquireTokenWithRefreshToken(response.refreshToken, sampleParameters.clientId, sampleParameters.clientSecret, resource, function(refreshErr, refreshResponse) {
if (refreshErr) {
message += 'refreshError: ' + refreshErr.message + '\n';
}
message += 'refreshResponse: ' + JSON.stringify(refreshResponse);
res.send(message);
});
});
});
app.listen(3000);
console.log('listening on 3000');
https://github.com/AzureAD/azure-activedirectory-library-for-nodejs/blob/master/sample/website-sample.js
As I known, I suggest you can follow the two documents below as references to get start.
Web App Sign In & Sign Out with Azure AD https://azure.microsoft.com/en-us/documentation/articles/active-directory-devquickstarts-openidconnect-nodejs/
Integrating Azure AD into a NodeJS web application https://azure.microsoft.com/en-us/documentation/samples/active-directory-node-webapp-openidconnect/
For developing easier, you can try to use the node package passport-azure-ad(https://github.com/AzureAD/passport-azure-ad) that is the one strategy of passport (http://passportjs.org/) for NodeJS to implement your needs.
I had similar problem and able to resolve it. After googling i made two changes in config.js.
issuer value set to false.
responseMode value changed from query to form_post.
config.js :
exports.creds = {
issuer : false,
realm : "<TENANT>",
returnURL: 'http://localhost:3000/auth/openid/return',
identityMetadata: 'https://login.microsoftonline.com/common/.well-known/openid-configuration', // For using Microsoft you should never need to change this.
clientID: '<CLIENT_ID>',
clientSecret: '<CLIENT_SECRET>', // if you are doing code or id_token code
skipUserProfile: true, // for AzureAD should be set to true.
responseType: 'id_token code', // for login only flows use id_token. For accessing resources use `id_token code`
responseMode: 'form_post', // For login only flows we should have token passed back to us in a POST
};

Resources