OAuth authentication with Azure Active Directory app - node.js

I am trying to run the OAuth example in the botbuilder nodejs documentation at https://github.com/Microsoft/BotBuilder/blob/master/Node/examples/basics-oauth/app.js
I have set up the Azure Active Directory v1 application on Azure with graph api access and have added the OAuth connection to my bot. The connectionName to be used in the code below is ready with me.
But when I run the code which is taken as is from the documentation (the github link provided above), I am getting:
TypeError: connector.getUserToken is not a function
I have run this both on emulator and on webchat channel and getting the same error.
// Create your bot with a function to receive messages from the user
var bot = new builder.UniversalBot(connector, function (session) {
if (session.message.text == 'signout') {
// It is important to have a SignOut intent
connector.signOutUser(session.message.address, connectionName, (err, result) => {
if (!err) {
session.send('You are signed out.');
} else {
session.send('There was a problem signing you out.');
}
});
} else {
// First check whether the Azure Bot Service already has a token for this user
connector.getUserToken(session.message.address, connectionName, undefined, (err, result) => {
if (result) {
// If there is already a token, the bot can use it directly
session.send('You are already signed in with token: ' + result.token);
} else {
// If there not is already a token, the bot can send an OAuthCard to have the user log in
if (!session.userData.activeSignIn) {
session.send("Hello! Let's get you signed in!");
builder.OAuthCard.create(connector, session, connectionName, "Please sign in", "Sign in", (createSignInErr, signInMessage) =>
{
if (signInMessage) {
session.send(signInMessage);
session.userData.activeSignIn = true;
} else {
session.send("Something went wrong trying to sign you in.");
}
});
} else {
// Some clients require a 6 digit code validation so we can check that here
session.send("Let's see if that code works...");
connector.getUserToken(session.message.address, connectionName, session.message.text, (err2, tokenResponse) => {
if (tokenResponse) {
session.send('It worked! You are now signed in with token: ' + tokenResponse.token);
session.userData.activeSignIn = false;
} else {
session.send("Hmm, that code wasn't right");
}
});
}
}
});
}
})

According to your error message, could you please double check your local botbuiler version, as the getUserToken function is added in 3.15.0, and you can find the definition at https://github.com/Microsoft/BotBuilder/blob/botbuilder%403.15.0/Node/core/src/bots/ChatConnector.ts, which doesn't show up before this version.

Related

Get Token for Azure ADv2 Rest Api Protected

I create an WebApi using visual studio and the wizard, for a protected api.
The result was an new application in the Azure Portal, and a configuration file jsonconfig (I am using netcore 2.2)
The web api is very simple a part of the code is
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value2" };
}
Now I am trying to get a token to call the api, using the next code
var msalConfig = {
auth: {
clientId: '83a8a6ee-afd5-41d3-92bd-2a6352cff7da', //This is your client ID
authority: "https://login.microsoftonline.com/d7124a8f-3301-4c72-9231-4bb39d8b95a3" //This is your tenant info
},
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: true
}
};
and the calling
var requestObj2 = {
scopes:["https://xxxxxx.com/Test2019/user_impersonation"]
};
var myMSALObj = new Msal.UserAgentApplication(msalConfig);
function signIn() {
myMSALObj.loginPopup(requestObj).then(function (loginResponse) {
//Successful login
showWelcomeMessage();
//Call MS Graph using the token in the response
acquireTokenPopupAndCallMSGraph();
}).catch(function (error) {
//Please check the console for errors
console.log(error);
});
}
and at the end
function acquireTokenPopupAndCallMSGraph() {
//Always start with acquireTokenSilent to obtain a token in the signed in user from cache
myMSALObj.acquireTokenSilent(requestObj2).then(function (tokenResponse) {
console.log(tokenResponse.accessToken);
alert('autenticado');
callMSGraph(graphConfig.graphMeEndpoint, tokenResponse.accessToken, graphAPICallback);
}).catch(function (error) {
console.log(error);
// Upon acquireTokenSilent failure (due to consent or interaction or login required ONLY)
// Call acquireTokenPopup(popup window)
if (requiresInteraction(error.errorCode)) {
myMSALObj.acquireTokenPopup(requestObj).then(function (tokenResponse) {
callMSGraph(graphConfig.graphMeEndpoint, tokenResponse.accessToken, graphAPICallback);
}).catch(function (error) {
console.log(error);
});
}
});
}
Everything works but the token generated when used in postman in the Authorization Header ="Bearer Token"
Does not work.
Please any advice on how to get the token.. :(
Thanks!
The way you get access token is correct. Replace the value of scopes by scopes:["83a8a6ee-afd5-41d3-92bd-2a6352cff7da/.default"] and try again.
If this still doesn't work, paste the error message here.
Here is a complete example regarding calling the backend web api using access token.

how to retrieve the msal object from cache location : local storage?

First things first, I am below a novice level when it comes to Nodejs/msal/azure b2c and I am trying to understand the flow.
I started off with this sample here: https://azure.microsoft.com/en-us/resources/samples/active-directory-b2c-javascript-msal-singlepageapp/
I am using msal.js with azure ad b2c in a Nodejs application. I am redirecting the user after signing in via sign-in policy to a different page where I have my other policies.
//index.html
var clientApplication = new Msal.UserAgentApplication(applicationConfig.clientID, applicationConfig.authority, authCallback, { logger: logger, cacheLocation: 'localStorage' });
function authCallback(errorDesc, token, error, tokenType) {
if (token) {
logMessage(token + ":" + token);
}
else {
logMessage(error + ":" + errorDesc);
}
}
This is my onclick login function in the index.html. The 'test(accessToken)' method does the redirection to the backend node js routes where I store the accesstoken in a session variable and the method renders to a different page(test.ejs) where my other b2c policies are stored.
function login() {
clientApplication.loginPopup(applicationConfig.b2cScopes).then(function (idToken) {
clientApplication.acquireTokenSilent(applicationConfig.b2cScopes).then(function (accessToken) {
test(accessToken);
}, function (error) {
clientApplication.acquireTokenPopup(applicationConfig.b2cScopes).then(function (accessToken) {
updateUI();
}, function (error) {
logMessage("Error acquiring the popup:\n" + error);
});
})
}, function (error) {
logMessage("Error during login:\n" + error);
});
}
Now my question is how can I retrieve the current state of the clientApplication Msal.UserAgentApplication object in my other view(test.ejs) to do something like this :
clientApplication.acquireTokenSilent(applicationConfig.b2cScopes).then(function (accessToken) {
logMessage(accessToken);
}

Azure BotBuilder - How to get the user information of the OAuth Connection Settings

I've created a Azure Web App Bot and added a OAuth Connection Setting which takes the user to Salesforce. Everything works well, I'm able to authenticate the user through my bot and also, I can get the access token from Salesforce.
Problem
Can someone help me to get the user information from Salesforce? Because, I am able to get the access token alone and not sure, how to get the user id from Salesforce.
I've written the below code,
var salesforce = {};
salesforce.signin = (connector, session, callback) => {
builder.OAuthCard.create(connector,
session,
connectionName,
"Sign in to your Salesforce account",
"Sign in",
(createSignInErr, createSignInRes) => {
if (createSignInErr) {
callback({
status: 'failure',
data: createSignInErr.message
});
return;
}
callback({
status: 'success',
data: createSignInRes
});
});
};
salesforce.getUserToken = (connector, session, callback) => {
connector.getUserToken(session.message.address,
connectionName,
undefined,
(userTokenErr, userTokenResponse) => {
if (userTokenErr) {
callback({
status: 'failure',
data: userTokenErr.message
});
return;
}
callback({
status: 'success',
data: userTokenResponse
});
});
};
salesforce.accessToken = (connector, session, callback) => {
salesforce.getUserToken(connector, session, (userTokenResponse) => {
if (userTokenResponse.status == 'failure') {
// If the user token is failed, then trigger the sign in card to the user.
salesforce.signin(connector, session, (signinResponse) => {
// If the sign in is failed, then let the user know about it.
if (signinResponse.status == 'failure') {
session.send('Something went wrong, ', signinResponse.message);
return;
}
// If the sign in is success then get the user token and send it to the user.
salesforce.getUserToken(connector, session, (newUserTokenResponse) => {
if (newUserTokenResponse.status == 'failure') {
session.send('Something went wrong, ', newUserTokenResponse.message);
return;
}
callback(newUserTokenResponse);
return;
});
});
}
callback(userTokenResponse);
});
};
I can get the userTokenResponse here. But I need Salesforce user id so that I can start interacting with Salesforce behalf of the user.
If you have only OAuth access token you may query details about the user by invoking http GET against:
https://login.salesforce.com/services/oauth2/userinfo for PROD or
https://test.salesforce.com/services/oauth2/userinfo for sandbox
Add only Authorization: Bearer Y0UR0AUTHTOKEN to the header of the http GET request.
Based on my recent test the result returned from the server looks like:
{
"sub": "https://test.salesforce.com/id/[organizationid]/[userId]",
"user_id": "000",
"organization_id": "000",
"preferred_username": "me#mycompany.com",
"nickname": "myNick",
"name": "name lastname",
"urls": {
...
},
"active": true,
"user_type": "STANDARD",
...
}
You don't need a userId to get the user information where an accessToken is enough. I've installed jsforce and used the below code to get the identity information.
Solved by doing,
const jsforce = require('jsforce');
var connection = new jsforce.Connection({
instanceUrl: instanceUrl,
sessionId: accessToken
});
connection.identity((error, response) => {
if(error) {
callback({
status: 'failure',
message: error.message
});
return;
}
callback({
staus: 'success',
data: response
});
});

Auth0-js Custom Login - 401 Unauthorized

I've been working off of this React Quickstart on auth0 https://auth0.com/docs/quickstart/spa/react/02-custom-login , trying to implement a custom login. When I try to Login I get a 401 Unauthorized Error and when I try to Sign Up I get the same alert error but the user does get created and I get redirected to the home page. Mind you everything works fine using the Lock Widget but when I try to do it with the custom login it doesn't.
Here's the AuthService code which is the most relevant I feel. The Login component simply calls the login and signup methods.
export default class SocialAuthService extends EventEmitter {
constructor(clientId, domain) {
super()
// Configure Auth0
this.auth0 = new auth0.WebAuth({
clientID: 'clientID',
domain: 'domain',
responseType: 'token id_token',
redirectUri: 'http://localhost:3000/login'
})
this.login = this.login.bind(this)
this.signup = this.signup.bind(this)
this.loginWithGoogle = this.loginWithGoogle.bind(this)
this.loginWithTwitter = this.loginWithTwitter.bind(this)
this.loginWithFacebook = this.loginWithFacebook.bind(this)
}
login(username, password) {
this.auth0.client.login({
realm: 'Username-Password-Authentication',
username,
password
}, (err, authResult) => {
if (err) {
alert('Error: ' + err.description)
return
}
if (authResult && authResult.idToken && authResult.accessToken) {
this.setToken(authResult.accessToken, authResult.idToken)
browserHistory.replace('/home')
}
})
}
signup(email, password){
this.auth0.redirect.signupAndLogin({
connection: 'Username-Password-Authentication',
email,
password,
}, function(err) {
if (err) {
alert('Error: ' + err.description)
}
})
}
parseHash(hash) {
this.auth0.parseHash({ hash }, (err, authResult) => {
if (authResult && authResult.accessToken && authResult.idToken) {
this.setToken(authResult.accessToken, authResult.idToken)
browserHistory.replace('/home')
this.auth0.client.userInfo(authResult.accessToken, (error, profile) => {
if (error) {
console.log('Error loading the Profile', error)
} else {
this.setProfile(profile)
}
})
} else if (authResult && authResult.error) {
alert('Error: ' + authResult.error)
}
})
}
loggedIn() {
// Checks if there is a saved token and it's still valid
const token = this.getToken()
return !!token && !isTokenExpired(token)
}
setToken(accessToken, idToken) {
// Saves user access token and ID token into local storage
localStorage.setItem('access_token', accessToken)
localStorage.setItem('id_token', idToken)
}
setProfile(profile) {
// Saves profile data to localStorage
localStorage.setItem('profile', JSON.stringify(profile))
// Triggers profile_updated event to update the UI
this.emit('profile_updated', profile)
}
getProfile() {
// Retrieves the profile data from localStorage
const profile = localStorage.getItem('profile')
return profile ? JSON.parse(localStorage.profile) : {}
}
getToken() {
// Retrieves the user token from localStorage
return localStorage.getItem('id_token')
}
logout() {
// Clear user token and profile data from localStorage
localStorage.removeItem('id_token')
localStorage.removeItem('profile')
}
loginWithGoogle() {
this.auth0.authorize({
connection: 'google-oauth2'
})
}
loginWithTwitter() {
this.auth0.authorize({
connection: 'twitter'
})
}
loginWithFacebook() {
this.auth0.authorize({
connection: 'facebook'
})
}
}
And this is the error:
Object
code
:
"access_denied"
description
:
"Unauthorized"
original
:
Error: Unauthorized at Request.<anonymous> (http://localhost:3000/static/js/bundle.js:49311:20) at Request.Emitter.emit (http://localhost:3000/static/js/bundle.js:49954:21) at XMLHttpRequest.xhr.onreadystatechange (http://localhost:3000/static/js/bundle.js:49616:11)
statusCode
:
401
statusText
:
"Unauthorized"
Any ideas on why I'm not able to Login ?
Not sure if you got an answer, but I was running into the same problem and it was because the backend wasn't able to properly decode the JWT. Custom Login signs with a RS256 token while it seems Lock signs with HS256. You have to decode these differently in your backend.
Here's a python example
Auth0.js version 8 verifies ID tokens during authentication transactions. Only tokens which are signed with the RS256 algorithm can be verified on the client side, meaning that your Auth0 client must be configured to sign tokens with RS256. See the auth0.js migration guide for more details.
I ran into a similar issue and my fix was switching the "Application Type" setting in Auth0 from "Regular Web Application" to "Single Page Application".
The Auth0 React SDK docs make it clear that you have to register your app as a single-page application in order for Auth0 to configure the appropriate settings, so that your web client can make successful requests to their endpoints.

users.list returns 403 Error: Not Authorized to access this resource/api

I am trying to retrieve a list of users using the node.js googleapis library and a service account.
I followed this guide to 'Perform Google Apps Domain-Wide Delegation of Authority'. There are examples for Java and Python, but unfortunately not for node.js, which seems to work rather differently.
I tried following the quickstart and completed the first two steps, but then it uses a manual OAuth flow instead of a service account.
So I tried to follow the example here to authorize using a service account. That all seems to work until I send the request, then I get an error: Error: Not Authorized to access this resource/api with code: 403.
Here's my code:
var google = require('googleapis'),
GoogleAuth = require('google-auth-library'),
authFactory = new GoogleAuth(),
admin = google.admin('directory_v1')
authFactory.getApplicationDefault(function (err, authClient) {
console.log('GOT APPLICATION DEFAULT', authClient)
if (err) {
console.log('Authentication failed because of ', err);
return;
}
if (authClient.createScopedRequired && authClient.createScopedRequired()) {
console.log('SCOPE REQUIRED')
var scopes = ['https://www.googleapis.com/auth/admin.directory.user'];
authClient = authClient.createScoped(scopes);
}
var request = {
auth: authClient,
domain: 'mydomain.com'
};
console.log('request:', request)
admin.users.list(request, function (err, result) {
if (err) {
console.log('admin.users.list error', err);
} else {
console.log(result);
}
});
});
What have I missed please?
After several hours of experimenting I came to the conclusion that this particular API cannot be accessed with a service account. Although it is not explicitly stated in the docs anywhere that I could find, the quickstart seems to overcome this limitation by using an OAuth process and then storing in a file the tokens required to authorize future requests. If I'm wrong please add a better answer!
My solution is to use the quickstart project to generate those tokens and then add the credentials and tokens from the quickstart to my project and use them whenever my server starts, something like:
let tokens = require('./credentials/tokens.json'),
credentials = require('./credentials/oauth_credentials.json'),
clientSecret = credentials.installed.client_secret,
clientId = credentials.installed.client_id,
redirectUrl = credentials.installed.redirect_uris[0],
google = require('googleapis'),
GoogleAuth = require('google-auth-library'),
authFactory = new GoogleAuth(),
admin = google.admin('directory_v1'),
oauth2Client = new authFactory.OAuth2(clientId, clientSecret, redirectUrl);
oauth2Client.credentials = tokens;
let request = {
auth: oauth2Client,
domain: 'coachaxis.com'
};
console.log('request:', request)
admin.users.list(request, function (err, result) {
if (err) {
console.log('admin.users.list error', err);
} else {
console.log(result);
}
});
It's not elegant but it works.

Resources