I am developing a server less application using azure functions using Node js. Function app auth via azure active directory. My scenario is I want to get particular user data ex(email,username etc) by using his email or username during execution of one of http trigger function. There are several options that I already checked (ad and activedirectory libraries. These library seems not updated and I want to know is there any way to do this by using azure's Node Js SDK?
I am seeking a solution for without JWT. because, when 'function' need to get some user information function doesn't have any token.here user data requested by the function app not the user.Hence user data only use by the function app those info not exposed to user.
for Example: We can get user data by 'querying' user table when we need to get user data inside of some application running MySQL. If we specify email we can query.
I am asking same thing by using azure active directory.
You can get the access token using client credential flow(not related to specific user) in your function.
const APP_ID = '[COPIED_APP_ID]';
const APP_SECERET = '[COPIED_APP_SECRET]';
const TOKEN_ENDPOINT ='https://login.microsoftonline.com/[COPIED_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);
});
Reference:
HOW TO — Get an Access Token for Microsoft Graph API using Node.JS
Then you can use this token to call Microsoft Graph API.
Related
I am trying to do a microsoft sso, where any user can use his microsoft account to sign in to the app. Now, if the user is part of the application users it works perfectly fine, but when I try to log in with my personal one, it gives me an error message saying "The account does not exist in this organization".
My endpoint for the sso:
const REDIRECT_URI = "http://localhost:3000/redirect";
const cca = new msal.ConfidentialClientApplication(config);
const scopes = ["user.read"];
router.get("/auth", (req, res) => {
// Construct a request object for auth code
const authCodeUrlParameters = {
scopes: scopes,
redirectUri: REDIRECT_URI,
};
// Request auth code, then redirect
cca
.getAuthCodeUrl(authCodeUrlParameters)
.then((response) => {
return res.send(response);
})
.catch((error) => res.send(error));
});
router.get("/redirect", (req, res) => {
// Use the auth code in redirect request to construct
// a token request object
const tokenRequest = {
code: `${req.query.code}`,
scopes: scopes,
redirectUri: REDIRECT_URI,
};
// Exchange the auth code for tokens
cca
.acquireTokenByCode(tokenRequest)
.then(async (response) => {
res.send(response);
})
.catch((error) => res.status(500).send(error));
});
It gives me an error message saying, "The account does not exist in this organization".
The error message shows that your personal Microsoft account is not associated with the organization that the application is using for authentication. Microsoft provides two different authentication services: Microsoft Account (MSA) and Azure Active Directory (AAD).
By using Guest method, you can use your personal account by assigning the roles.
If your organization has set up the application to use AAD for authentication, then you need to have an AAD account in the same organization in order to log in.
Create an AAD account and map it with the same email address as your MSA. This will allow you to log into the application using your AAD account.
And if you don't want to create an AAD account, you can use a different email address (such as a work email address) that is already associated with the organization's AAD.
Steps to create Microsoft SSO
Register your application to access Microsoft Graph API, and you need to first register your application in the Microsoft Developer Portal and obtain a client ID and secret.
The first step in the authorization code grant flow is to redirect the user to the Microsoft authorization endpoint. The user must sign in and grant the permissions that your application requests.
After the user grants permission, Microsoft will redirect the user back to your application with an authorization code.
Your application can then exchange this code for an access token by making a POST request to the Microsoft token endpoint.
Once you have an access token, you can use it to call the Microsoft Graph API on behalf of the user. To do this, you'll need to add the access token to the Authorization header of your API request.
Access tokens are short-lived and need to be refreshed after a certain time period. You can use the refresh token to obtain a new access token without requiring the user to sign in again.
async function getAccessToken(code) {
const tokenRequestBody = querystring.stringify({
grant_type: 'authorization_code',
code: code,
redirect_uri: redirectUri,
client_id: clientId,
client_secret: clientSecret,
});
const tokenResponse = await axios.post('https://login.microsoftonline.com/common/oauth2/v2.0/token', tokenRequestBody);
return tokenResponse.data.access_token;
}
async function getUserProfile(accessToken) {
const userProfileResponse = await axios.get('https://graph.microsoft.com/v1.0/me', {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
return userProfileResponse.data;
}
async function main() {
const code = 'AUTHORIZATION_CODE';
const accessToken = await getAccessToken(code);
const userProfile = await getUserProfile(accessToken);
console.log(userProfile);
}
References taken from
Nodejs-webapp-msal
ADD
We are using firebase cloud functions in our application. We have 2 firebase functions: 1. A regular firebase cloud function that runs at regular intervals, 2. A firebase http function that is invoked by function #1 and can also be invoked from outside the application environment (in future). We have a use case where we have #1 firebase cloud function processes thousands of user records and performs some specific operations by invoking the #2 firebase http function. Our requirement now is to authenticate the invocation of the firebase http function. Both the firebase functions are in the same service account.
Following the steps in the documentation : https://cloud.google.com/functions/docs/securing/authenticating#authenticating_function_to_function_calls we tried to create an Id token in the calling function and included the ID token in an Authorization: Bearer ID_TOKEN header in the request to the http function.
Our code to obtain the id token. We tried using the firebase receiving function url, our firebase project url
const { URL } = require('url')
const targetAudience = new URL(url)
const client = await auth.getIdTokenClient(targetAudience)
const idToken = await client.request({ url })
Our code to verify the token
const tokenId = request.get('Authorization').split('Bearer ')[1]
const decodedIdToken = await admin.auth().verifyIdToken(tokenId)
We get back an idToken correctly, but when that token is verified, we always get this error :
Error while verifying Firebase ID token: { Error: Firebase ID token has incorrect "aud" (audience) claim. Expected "<firebase-project-name>" but got "https://<cloud-function-endpoint>". Make sure the ID token comes from the same Firebase project as the service account used to authenticate this SDK. See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.
const targetAudience = new URL(url) - for the target audience we tried the firebase function URL of the http function, the name of the project, adding https to the name of the project, but we always keep getting the same error Firebase ID token has an incorrect "aud" claim
Any pointers on what we could we doing wrong? Has anyone tried to authenticate invocation to one cloud function when the calling function is another cloud function in the same service account.
try to generate the idToken this way
const serviceAccountEmail = '...';
const generateIdToken = async () => {
const tokens = await admin.options.credential?.getAccessToken();
const projectId =
const res = await fetch(`https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${serviceAccountEmail}:generateIdToken`, {
method: 'POST',
headers: {
authorization: `Bearer ${tokens?.access_token}`,
'content-type': 'application/json',
},
body: JSON.stringify({
delegates: [
],
audience: projectId,
includeEmail: true
}),
});
return res.json();
}
Roles needed on the service account
Service Account Token Creator
Service Account User
Google APIs enable on the project
IAM Service Account Credentials API
it seems the admin.auth().verifyToken is expecting the audience to be the projectId instead of the url of the function endpoint.
normally what I do when I want a function to be private(only invoke from other functions) I make it "require authentication(Manage authorized users with Cloud IAM.)" then I don't even have to verify the id token in my implementation.
On my express server I make queries to an external API using its own token. When I log in to my server I request a token to the external API based on the user who logged in, and I keep the token of the external API in the token of my express server.
Each user gets different data according to their token from the external api, for queries that require external API information, I read the received token and get the external API token to send it through headers with axios, for example:
const LoginUser = (request, response) {
axios.post('/ExternalApi/auth',request.body)
.then( data =>{
const payload = {
...
tokenExternalApi: data.token
}
const token = jwt.sign(payload, ...)
return response.status(200).json(token)
})
}
const getData = (req, response){
const tokenFromClient = req.headers.authorization
//Function extract tokenExternalApi from payload Token
const tokenExternalApi = getTokenExternl(tokenFromClient )
axios.get(`/urlExternalApi`, { headers:
{ Authorization: tokenExternalApi }}
).then(res => {
return response.status(200).json(res.data)
})
}
Is this the correct approach to managing external apis tokens or is there a cleaner way to do it?
Here is my sample code that I use for hit an external API within function in node js using axios
first time you should install axios npm install axois
const axios = require('axios');
async yourFunction(){
axios({
method: 'POST',
url: "http://yoururl.com",
data: {
name: '+62'+phoneNumber,
number: '+62'+phoneNumber,
message: 'success',
}
});
}
In my personal opinion, this seems to be a clean approach.
But keep in mind that tokens are visible to users, so the fact is your users can decode the token, view tokenExternalApi, know that you are using an external API in the backend and directly make calls to ExternalApi using that token, provided they have the know-how of it. If you understand this fact and are fine with it, then this works.
Otherwise, you can consider encoding the token before sending it to the user or store it on the server-side session.
I am developing my first app for Google Cloud Platform.
In particular, I am using Node.js as base-framework. Google itself provides Node.js client libraries to interact with their services.
For instance, this code is able to create a new bucket within Cloud Storage:
var storage = require('#google-cloud/storage')();
var bucket = storage.bucket('albums');
bucket.create(function(err, bucket, apiResponse) {
if (!err) {
// The bucket was created successfully.
}
});
//-
// If the callback is omitted, we'll return a Promise.
//-
bucket.create().then(function(data) {
var bucket = data[0];
var apiResponse = data[1];
});
However, if I deploy this code on Google Application Engine, the action above is done using a service account (I suppose, at least) and not as end-user, even after an OAuth authentication, thus ignoring the IAM policies in place.
How could I avoid this problem, and use an user-centric flow for my requested? Can I use the Identiy-Aware Proxy? If yes, how?
Update
To make my question more understandable:
Consider this code:
router.get('/test2', oauth2.required, (req, res, next) => {
const Storage = require('#google-cloud/storage');
// Creates a client
const storage = new Storage();
// Lists all buckets in the current project
storage
.getBuckets()
.then(results => {
const buckets = results[0];
console.log('Buckets:');
buckets.forEach(bucket => {
console.log(bucket.name);
});
res.send("There are " + buckets.length + " buckets.");
})
.catch(err => {
res.send(err);
});
});
This route can be invoked if a given user has already signed in via OAuth2.
I would like to invoke the getBuckets() method passing the OAuth accessToken to perform this operation impersonating the user itself.
In this way, the action cannot skip the IAM rules in place in GCP for that given user currently logged.
I didi try:
const storage = new Storage({
auth: {
'bearer': req.user.accessToken
}});
But it does not work. The application still uses my default account.
You have two options to make sure your requests are allowed:
Grant the necessary permissions on the bucket and/or objects to your service account. This works if you control the data, but not if your application has to function with buckets/objects the user controls.
Do the "three legged Oauth" flow to get permission to make calls on behalf of the user. Unfortunately the client library you are calling doesn't support this. I don't know where you got the auth:{'bearer':...} from, but even if that did work the token you are passing wouldn't have the required scopes to access the bucket.
This autogenerated library does support three-legged auth. You'd use it soemthing like:
var google = require('googleapis');
var OAuth2 = google.auth.OAuth2;
var oauth2Client = new OAuth2(
YOUR_CLIENT_ID,
YOUR_CLIENT_SECRET,
YOUR_REDIRECT_URL
);
function handle_requests_to_redirect_url() {
// extract code query string parameter
token = await oauth2Client.getToken(code);
// Save token
}
if (no_known_auth_token_for_user()) {
// redirect user to
oauth2Client.generateAuthUrl({access_type:'offline', scope: ['https://www.googleapis.com/auth/devstorage.read_write']});
// after clicking OK, user is redirected to YOUR_REDIRECT_URL
}
var token = get_saved_token_for_this_user();
oauth2Client.setCredentials(token);
var storage = google.storage({version: 'v1', auth: oauth2Client});
storage.buckets.list(function (e,res) {
console.log(res);
});
I've been looking through the Google NodeJS API docs, but I don't see one listed for the Contacts API. Am I missing something or is that not included in the module?
Google's official API for NodeJS doesn't use Contacts API. They use instead the People API. If you need to access "Other Contacts", you will need Contacts API.
You can still connect with Contacts API using the official googleapis library if you're already using it for other purposes by sending a request to the Contacts API after creating the auth client. If you're not using the googleapis library, it might be an overkill, and it's better to use the other libraries suggested by other answer.
Given that you already have the access token of the user (e.g. if you generated it using Passport, here's the code:
const {google} = require("googleapis");
const authObj = new google.auth.OAuth2({
access_type: 'offline',
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
});
Refresh access token automatically before it expires
authObj.on('tokens', (tokens) => {
const access_token = tokens.access_token
if (tokens.refresh_token){
this.myTokens.refreshToken = tokens.refresh_token
// save refresh token in the database if it exists
}
this.myTokens.accessToken = tokens.access_token
// save new access token (tokens.access_token)
}
authObj.setCredentials({
access_token:this.myTokens.accessToken,
refresh_token:this.myTokens.refreshToken,
});
Make the request to Contacts API:
authObj.request({
headers:{
"GData-Version":3.0
},
params:{
"alt":"json",
//"q":"OPTIONAL SEARCH QUERY",
//"startindex":0
"orderby":"lastmodified",
"sortorder":"descending",
},
url: "https://www.google.com/m8/feeds/contacts/default/full"
}).then( response => {
console.log(response); // extracted contacts
});
According to Google NodeJS API for Google Contacts API, the links below maybe could help you out:
https://github.com/jimib/nodejs-google-contacts
https://github.com/elentok/gcontacts
https://github.com/mattnull/node-googlecontacts