How to implement OAuth 2 Circuit REST API for Bots? To use the client_id and client_secret. Thank you.
See https://circuit.github.io/oauth.html#client_credentials on the HTTP request to get the token. You can manually perform the /oauth/token request to get a token, or use any OAuth 2.0 library. The perform regular HTTP GET/POST requests using this OAuth token.
Here is an example that uses simple-oauth2 to get the token and then node-fetch to get the conversations.
const simpleOauth2 = require('simple-oauth2');
const fetch = require('node-fetch');
const DOMAIN = 'https://circuitsandbox.net';
const credentials = {
client: {
id: '<client_id>',
secret: '<cient_secret>'
},
auth: {
tokenHost: DOMAIN
}
};
// Initialize the OAuth2 Library
const oauth2 = simpleOauth2.create(credentials);
(async () => {
try {
const { access_token: token } = await oauth2.clientCredentials.getToken({scope: 'ALL'})
console.log('Access Token: ', token);
const convs = await fetch(`${DOMAIN}/rest/conversations`, {
headers: { 'Authorization': 'Bearer ' + token },
}).then(res => res.json());
console.log('Conversations:', convs);
} catch (err) {
console.error(err);
}
})();
Related
I have a backend in Nodejs using Axios for my API calls. I need to implement Azure Authentication to get a token so I followed the sample below:
https://learn.microsoft.com/en-us/azure/active-directory/develop/quickstart-v2-nodejs-webapp-msal?WT.mc_id=Portal-Microsoft_AAD_RegisteredApps
The sample uses express and has redirects to first get and authorization and then a token, I have been trying to find a sample with Axios however I couldn't find one.
This is what I have so far, the idea is using the result to get a token,any guidance is much appreciate it.
const msal = require('#azure/msal-node');
const REDIRECT_URI = "http://localhost:3000/";
const LOGIN = "https://login.microsoftonline.com/";
const config = {
auth: {
clientId: "12345678910",
authority: "https://login.microsoftonline.com/12345678910",
clientSecret: "Secret",
knownAuthorities: ["https://login.microsoftonline.com/12345678910"
]
}
};
const pca = new msal.ConfidentialClientApplication(config);
module.exports = {
async getAzureAdToken(){
try {
let instance = axios.create({baseURL: LOGIN});
const authCodeUrlParameters = {
scopes: ["user.read"],
redirectUri: REDIRECT_URI
};
pca.getAuthCodeUrl(authCodeUrlParameters).then((response) =>{
let url = response.substring(LOGIN.length);
instance.get(url).then((result) =>{
});
}).catch((error) => console.log(JSON.stringify(error)));
} catch (error) {
throw error
}
},
You could use client credentials flow to get access token with axios. Client credentials flow permits a web service (confidential client) to use its own credentials, instead of impersonating a user, to authenticate when calling another web service. In the client credentials flow, permissions are granted directly to the application itself by an administrator. We need to add application permissions in API Permission.
Test in Postman:
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
client_id=<client_id>
&scope=https://graph.microsoft.com/.default
&client_secret=<client_secret>
&grant_type=client_credentials
Code using Nodejs:
// Replace these values from the values of you app
const APP_ID = '[APP_ID/CLIENT_ID]';
const APP_SECERET = '[CLIENT_SECRET]';
const TOKEN_ENDPOINT ='https://login.microsoftonline.com/[TENANT_ID]/oauth2/v2.0/token';
const MS_GRAPH_SCOPE = 'https://graph.microsoft.com/.default';
const axios = require('axios');
const qs = require('qs');
const postData = {
client_id: APP_ID,
scope: MS_GRAPH_SCOPE,
client_secret: APP_SECERET,
grant_type: 'client_credentials'
};
axios.defaults.headers.post['Content-Type'] =
'application/x-www-form-urlencoded';
let token = '';
axios
.post(TOKEN_ENDPOINT, qs.stringify(postData))
.then(response => {
console.log(response.data);
})
.catch(error => {
console.log(error);
});
I am having problems getting the correct token for triggering my cloud function.
When testing through POSTMAN I get the token by running the following command:
gcloud auth print-identity-token
and my functions works correctly.
But on my server I am using the following code. I also do see the token but I get 401 with this token.
// Constants------------
const metadataServerTokenURL = 'http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience=';
async function gToken(){
let token='';
try{
// Fetch the token
const tokenResponse = await fetch(metadataServerTokenURL + 'https://'+process.env.CLOUD_URL, { //URL WITHOUT THE PATH
headers: {
'Metadata-Flavor': 'Google',
},
});
token = await tokenResponse.text();
} catch (err){
console.log(err);
}
return token;
}
---------EDIT-------
The calling function::
app.get("/", async function(req , res){
try {
const token = await getToken();
console.log(`Token: ${token}`);
const functionResponse = await fetch('https://'+process.env.CLOUD_URL+process.env.PATH_TO_FUNC, {
method: 'post',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`},
});
console.log(`Status: ${await functionResponse.status}`);
res.sendStatus(200);
} catch (err){
console.log(err);
res.status(400).send('Something went wrong')
}
})
My server is my NodeJS code running on AppEngine.
What am I doing wrong please?
----------EDIT 2--------------
I entered the two tokens received using two different ways, they show different information for some reason. Please see below::
Token from the server
Token using gcloud command locally (which works)::
Server code and cloud functions are both hosted in the same region, and are a part of the same project.
process.env.CLOUD_URL > "e****-***2-c******-e******2.cloudfunctions.net"
What #Charles and #John mentioned in the comment is correct. You should include the name of the receiving function in the audience:
As mentioned in the docs:
In the calling function, you'll need to create a Google-signed OAuth ID token with the audience (aud) set to the URL of the receiving function.
const metadataServerTokenURL = 'http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience=';
...
// Fetch the token
const tokenResponse = await fetch(metadataServerTokenURL + `https://${process.env.CLOUD_URL}/${FUNCTION_NAME}`, {
headers: {
'Metadata-Flavor': 'Google',
}
The audience should look like your HTTP trigger URL. If you decode your JWT ID token, aud looks like this:
{
"aud": "https://[REGION]-[PROJECT_ID].cloudfunctions.net/func-post",
"azp": "117513711437850867551",
"exp": 1614653346,
"iat": 1614649746,
"iss": "https://accounts.google.com",
"sub": "117513711437850867551"
}
My goal is to consume an API which has already been deployed with nopCommerce (I do not have dev access to the server - I am just a user). There is a sample client application here, but the code is in C#. I have a webapp deployed on an Azure server using node.js. The API uses the OAuth 2.0 Authorization Code grant type.
I did a bit of Googling and it appears that client_credentials is typically used for this type of server to server flow:
How does 2-legged oauth work in OAuth 2.0?
Using OAuth for server-to-server authentication?
There is also an answer here which suggests that I can manually retrieve a token and then store it on the server. This is what I'm currently doing while testing my code.
I also found this answer which appears to be asking the same question. Like the author of that post, I am able to get a token via Postman, but my node.js code fails.
OAuth2.0 for nopCommerce
I wrote a minimal example in node.js here.
import { config } from 'dotenv';
import * as path from 'path';
import fetch from 'cross-fetch';
const ENV_FILE = path.join(__dirname, '.env');
const loadFromEnv = config({ path: ENV_FILE });
export async function getCodeUrl() {
const params = {
client_id: <CLIENT_ID>,
redirect_uri: 'http://example.com',
response_type: 'code',
};
console.log(params);
const url = new URL(`http://example.com/OAuth/Authorize`);
Object.keys(params).forEach(( key ) => url.searchParams.append(key, params[key]));
const res = await fetch(url.href, { method: 'GET' });
return res;
}
export async function getToken(code: string) {
const url = new URL(`http://example.com/api/token`);
const options = {
form: {
client_id: <CLIENT_ID>,
client_secret: <CLIENT_SECRET>,
code,
grant_type: 'authorization_code',
redirect_ui: 'http://example.com',
},
headers: { 'content-type': 'application/x-www-form-urlencoded' },
method: 'POST',
};
console.log(options);
const res = await fetch(url.href, options);
console.log('res', res);
return res;
}
const test = async () => {
const codeUrlString = (await getCodeUrl()).url;
const code = (new URL(codeUrlString).searchParams.get('code'));
if (code) {
console.log('code', code);
const tokenResponse = await getToken(code);
console.log('token res', tokenResponse);
}
};
test();
I am successfully able to retrieve the authorization code, but when I use that in a POST request to get a token, I get this error:
{ error: 'invalid_client' }
I'd like to use this library to interact with the graph API for my AD - https://github.com/microsoftgraph/microsoft-graph-docs/blob/master/concepts/nodejs.md
However, all of the existing javascript libraries I've found to return access tokens expect a return URL to be passed in, as well as some other web-specific stuff, leading me to believe this is some kind of requirement on Microsoft's end.
Is there any good way to authenticate/receive an access token while running a backend node script (nothing web related) so that I can begin to make calls against the Microsoft Graph API? Thanks in advance for the advice.
BU0's answer didn't work correctly for me because Microsoft changed their way of using the graph API so I wasn't able to get all the data I needed. Here's how I did it using BU0 answer and this tutorial:
const request = require("request");
const endpoint = "https://login.microsoftonline.com/[Tenant]/oauth2/v2.0/token";
const requestParams = {
grant_type: "client_credentials",
client_id: "[ApplicationID]",
client_secret: "[Key]",
scope: "https://graph.microsoft.com/.default"
};
request.post({ url: endpoint, form: requestParams }, function (err, response, body) {
if (err) {
console.log("error");
}
else {
console.log("Body=" + body);
let parsedBody = JSON.parse(body);
if (parsedBody.error_description) {
console.log("Error=" + parsedBody.error_description);
}
else {
console.log("Access Token=" + parsedBody.access_token);
}
}
});
function testGraphAPI(accessToken) {
request.get({
url:"https://graph.microsoft.com/v1.0/users",
headers: {
"Authorization": "Bearer " + accessToken
}
}, function(err, response, body) {
console.log(body);
});
}
To run a back-end non-user-authenticated daemon connected to the Graph API, you want to use the app-only authentication flow. Here's a quick summary of the official steps:
Create your Azure AD Tenant. Note the yourtenant.onmicrosoft.com name, and copy this value down.
Register an application through the global Azure Active Directory blade's App Registrations section, not directly within the tenant properties. Copy the Application ID; we'll need it later.
Create a key tied to the registration and remember to copy it down. Once you click out, you can't get the key value back, so make sure to copy it.
Also update the registration's permissions to what you need, click Save, and then also hit the Grant Permissions button.
Make an HTTP request to the login.microsoftonline.com domain to obtain an access token.
Use the access token to make Graph API requests.
Here's a link to Microsofts Node.js example, and here's a link to the direct documentation on the HTTP call to make to retrieve an access token. And here's a super stripped-down example that will output the retrieved access token. Replace the [Tenant], [ApplicationID], and [Key] values:
const request = require("request");
const endpoint = "https://login.microsoftonline.com/[Tenant].onmicrosoft.com/oauth2/token";
const requestParams = {
grant_type: "client_credentials",
client_id: "[ApplicationID]",
client_secret: "[Key]",
resource: "https://graph.windows.net"
};
request.post({ url:endpoint, form: requestParams }, function (err, response, body) {
if (err) {
console.log("error");
}
else {
console.log("Body=" + body);
let parsedBody = JSON.parse(body);
if (parsedBody.error_description) {
console.log("Error=" + parsedBody.error_description);
}
else {
console.log("Access Token=" + parsedBody.access_token);
}
}
});
Once we have the access_token, we can call out to the Graph API. Assuming the apps permissions were configured correctly and applied from step #4, we can start making Graph API requests:
function testGraphAPI(accessToken) {
request.get({
url:"https://graph.windows.net/[Tenant]/users?api-version=1.6",
headers: {
"Authorization": accessToken
}
}, function(err, response, body) {
console.log(body);
});
}
I had somewhat of an issue for using the url string for the const endpoint
https://login.microsoftonline.com/[Tenant]/oauth2/v2.0/token
Instead, I passed tenant in this way instead from Microsoft graph api docs:
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize
Reference from docs -> Request an authorization code
Another way:
'use strict';
const axios = require('axios');
const qs = require('qs');
const accessTokenWithCredentials = (tenantId, clientId, clientSecret, resource) => {
const data = {
resource: resource,
grant_type: 'client_credentials',
};
return axios({
url: `https://login.windows.net/${tenantId}/oauth2/token`,
method: "post",
headers: { 'content-type': 'application/x-www-form-urlencoded' },
auth: {
username: clientId,
password: clientSecret,
},
data: qs.stringify(data)
}).catch(error => {
throw error;
})
};
To call the function:
accessTokenWithCredentials(<tenantId>, <clientId>, <clientSecret>, 'https://graph.microsoft.com').then(response => {
console.log(`Got access token`);
const token = JSON.stringify(response.data.access_token);
// do what you need to do
}).catch(err => {
console.log("err " + err);
throw err;
});
Getting a user profile info through curl
curl -i https://www.googleapis.com/userinfo/v2/me -H "Authorization: Bearer a-google-account-access-token"
Getting a user profile info through node https get request
const https = require('https');
function getUserData(accessToken) {
var options = {
hostname: 'www.googleapis.com',
port: 443,
path: '/userinfo/v2/me',
method: 'GET',
json: true,
headers:{
Authorization: 'Bearer ' + accessToken
}
};
console.log(options);
var getReq = https.request(options, function(res) {
console.log("\nstatus code: ", res.statusCode);
res.on('data', function(response) {
try {
var resObj = JSON.parse(response);
console.log("response: ", resObj);
} catch (err) {
console.log(err);
}
});
});
getReq.end();
getReq.on('error', function(err) {
console.log(err);
});
}
var token = "a-google-account-access-token";
getUserData(token)
How can I use this google node api client library to get the user profile info provided that I already have the access token? I can use the code above to get the profile but I thought it's probably better off to use the google api library to do it, but I can't figure out how to do that using this node google api client library.
A temporary access token can be acquired through playing this playground
You can retrieve the user profile using the google node API client library. In this case, please retrieve the access token and refresh token as the scope of https://www.googleapis.com/auth/userinfo.profile. The sample script is as follows. When you use this sample, please set your ACCESS TOKEN.
Sample script :
var google = require('googleapis').google;
var OAuth2 = google.auth.OAuth2;
var oauth2Client = new OAuth2();
oauth2Client.setCredentials({access_token: 'ACCESS TOKEN HERE'});
var oauth2 = google.oauth2({
auth: oauth2Client,
version: 'v2'
});
oauth2.userinfo.get(
function(err, res) {
if (err) {
console.log(err);
} else {
console.log(res);
}
});
Result :
{
id: '#####',
name: '#####',
given_name: '#####',
family_name: '#####',
link: '#####',
picture: '#####',
gender: '#####',
locale: '#####'
}
If I misunderstand your question, I'm sorry.
2021 Solution
This answer may divert from the originally asked question but I think it will be useful for some people who are getting google user information in the backend by generating AuthUrl and sending it to the client side and then receiving the data response in the call back URL after the user gives permission from the client side.
Some global declarations
import { google } from "googleapis";
const Oauth2Client = new google.auth.OAuth2(
googleCredentials.CLIENT_ID,
googleCredentials.CLIENT_SECRET,
googleCredentials.REDIRECT_URI
);
Generate the Auth URL with the scopes
const SCOPE = [
'https://www.googleapis.com/auth/userinfo.profile', // get user info
'https://www.googleapis.com/auth/userinfo.email', // get user email ID and if its verified or not
];
const auth_url = Oauth2Client.generateAuthUrl({
access_type: "offline",
scope: SCOPE,
prompt: "consent",
state: "GOOGLE_LOGIN",
});
return res.json({ url: auth_url }); // send the Auth URL to the front end
Get the user data in the callback
let code = req.query.code; // get the code from req, need to get access_token for the user
let { tokens } = await Oauth2Client.getToken(code); // get tokens
let oauth2Client = new google.auth.OAuth2(); // create new auth client
oauth2Client.setCredentials({access_token: tokens.access_token}); // use the new auth client with the access_token
let oauth2 = google.oauth2({
auth: oauth2Client,
version: 'v2'
});
let { data } = await oauth2.userinfo.get(); // get user info
console.log(data);
Feel free to discuss in the comments if there's any confusion or error
You can also decode the id_token that's in the response from oAuth2Client.getToken().
Remember that you need to have necessary scopes enabled for your app for the response to have this id_token. For an example, let's say we just need the user's email. So we'll use,
https://www.googleapis.com/auth/userinfo.email
To decode the token,
const response = await oAuth2Client.getToken(code);
const userInfo = JSON.parse(Buffer.from(response.tokens.id_token.split('.')[1], 'base64').toString());
console.log(userInfo);
This will output,
{
"iss": "https://accounts.google.com",
"azp": "1234987819200.apps.googleusercontent.com",
"aud": "1234987819200.apps.googleusercontent.com",
"sub": "10769150350006150715113082367",
"at_hash": "HK6E_P6Dh8Y93mRNtsDB1Q",
"hd": "example.com",
"email": "jsmith#example.com",
"email_verified": "true",
"iat": 1353601026,
"exp": 1353604926,
"nonce": "0394852-3190485-2490358"
}
This token simply is a JWT object which can be decoded eaisily. You can check this documentation and see even Google recommends this. The advantage of this method is that you don't have to make an extra request to Google APIs to get user's info.
Based on this anser: https://stackoverflow.com/a/46780714/140164
Here is another shorter way to achieve the same results.
const { google } = require('googleapis');
const oauth2Client = new google.auth.OAuth2()
const tokenInfo = await oauth2Client.getTokenInfo(*YourAccessToken*);
console.log(tokenInfo);