I want to use google pay API for passes with firebase cloud functions, but unfortunately, nodejs is bitterly missed in google-pay/passes-rest-samples and is not supported in the client libraries.
I was able to test the API in the PHP sample - that is my service account is up and linked to my merchant account, but I want to know how to use the API in nodejs:
1- How to get an access token and save a pass in the request call?
Tried the following but I'm always getting 401 status code:
a) Using google-auth-library-nodejs
// Create a new JWT client using the key file downloaded from the Google Developer Console
const auth = new google.auth.GoogleAuth({
keyFile: path.join(__dirname, 'serviceAccount.json'),
scopes: 'https://www.googleapis.com/auth/wallet_object.issuer',
});
const client = await auth.getClient();
const accessToken = (await client.getAccessToken()).token;
const result = (await axios.post(`https://walletobjects.googleapis.com/walletobjects/v1/loyaltyClass?strict=true`, payload,
{ headers: { Authorization: `Bearer ${accessToken}` } })
).data;
b) Using jsonwebtoken
const token = jwt.sign({ payload, typ: JWT_TYPE }, credentialJson.private_key,
{
algorithm: 'RS256',
audience: AUDIENCE,
issuer: SERVICE_ACCOUNT_EMAIL_ADDRESS,
});
Here is an example of how to achieve this with Node.js:
The relevant parts:
// Step 1: create a loyalty object
const loyaltyObject = await createLoyaltyObject();
// Step 2: define jwt claims
const claims = {
aud: 'google',
origins: [website],
iss: credentials.client_email,
typ: 'savetowallet',
payload: {
loyaltyObjects: [
{
id: loyaltyObject.id,
},
],
},
};
// Step 3: create and sign jwt
const token = jwt.sign(claims, credentials.private_key, { algorithm: 'RS256' });
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);
});
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 want to athenticate myself (React application) using cypress.js (https://www.cypress.io/). Is there a way to do it programatically with PKCE? As i was reading and looking into all examples - all of them are using implicit flow
I was trying to use solutions like https://www.npmjs.com/package/react-adal but with no success as it needs an implicit flow to be turned on
I was trying this as well: https://xebia.com/blog/how-to-use-azure-ad-single-sign-on-with-cypress/ with no success
I expected to programatically signin inside cypress and save user info and access_token to sessionStorage to be able to perform another api calls
I have not found a way to do this programmatically with PKCE per se, but with the MSAL 2.0 library (#azure/msal-browser on npm) I was able to fill in the account cache ahead of time so it thought it was already logged in. The process looks like this:
With a cy.task, send a request to Azure AD using the ROPC flow to get the tokens.
const scopes = [
'openid',
'profile',
'user.read',
'email',
'offline_access' // needed to get a refresh token
];
const formdata = new URLSearchParams({
'grant_type': 'password',
'scope': scopes.join(' '),
'client_info': 1, // returns an extra token that MSAL needs
'client_id': aadClientId,
'client_secret': aadClientSecret,
'username': aadUsername,
'password': aadPassword,
});
const response = await fetch(`https://login.microsoft.com/${aadTenantId}/oauth2/v2.0/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: formdata.toString(),
});
const tokens = await response.json();
Transform the tokens into the cache entries that MSAL wants (based on observing it in a real browser)
// The token tells us how many seconds until expiration;
// MSAL wants to know the timestamp of expiration.
const cachedAt = Math.round(new Date().getTime()/1000);
const expiresOn = cachedAt + tokens.expires_in;
const extendedExpiresOn = cachedAt + tokens.ext_expires_in;
// We can pull the rest of the data we need off of the ID token body
const id_token = JSON.parse(Buffer.from(tokens.id_token.split('.')[1], 'base64').toString('utf-8'));
const clientId = id_token.aud;
const tenantId = id_token.tid;
const userId = id_token.oid;
const name = id_token.name;
const username = id_token.preferred_username;
const environment = 'login.windows.net'; // 🤷♂️
const homeAccountId = `${userId}.${tenantId}`;
const cacheEntries = {};
// client info
cacheEntries[`${homeAccountId}-${environment}-${tenantId}`] = JSON.stringify({
authorityType: 'MSSTS',
clientInfo: tokens.client_info,
environment,
homeAccountId,
localAccountId: userId,
name,
realm: tenantId,
username,
});
// access token
cacheEntries[`${homeAccountId}-${environment}-accesstoken-${clientId}-${tenantId}-${token.scope}`] = JSON.stringify({
cachedAt: cachedAt.toString(),
clientId,
credentialType: "AccessToken",
environment,
expiresOn: expiresOn.toString(),
extendedExpiresOn: extendedExpiresOn.toString(),
homeAccountId,
realm: tenantId,
secret: tokens.access_token,
target: tokens.scope,
});
// id token
cacheEntries[`${homeAccountId}-${environment}-idtoken-${clientId}-${tenantId}-`] = JSON.stringify({
clientId,
credentialType: "IdToken",
environment,
homeAccountId,
realm: tenantId,
secret: tokens.id_token,
});
// refresh token
cacheEntries[`${homeAccountId}-${environment}-refreshtoken-${clientId}--`] = JSON.stringify({
clientId,
credentialType: "RefreshToken",
environment,
homeAccountId,
secret: tokens.refresh_token,
});
Use cy.window to store those in sessionStorage or localStorage, depending on how you have MSAL configured.
cy.task('login').then(cacheEntries => {
cy.window().then(window => {
for (let entry in cacheEntries) {
window.sessionStorage.setItem(entry, cacheEntries[entry]);
}
});
});
It's super fragile and not very pretty, but it works! The user that Cypress logs in as needs MFA to be disabled, of course.
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);
}
})();
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);