Teams bot SSO without dialogs framework. Invalid x5t claim - node.js

I'm following this tutorial:
https://learn.microsoft.com/en-us/learn/modules/msteams-sso/7-exercise-bots-sso
https://github.com/OfficeDev/TrainingContent/tree/master/Teams/80%20Using%20Single%20Sign-On%20with%20Microsoft%20Teams/Demos/02-learn-msteams-sso-bot
https://youtu.be/cmI06T2JLEg
https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/javascript_nodejs/24.bot-authentication-msgraph
The bot worked as expected. But I would like not to use the dialog framework. I'm having trouble adapting the model.
In the personal scope I reply to a message with an oauth card:
const oauthCard = await CardFactory.oauthCard(SsoConnectionName, undefined, undefined, undefined, {
id: 'random65jHf9276hDy47',
uri: `api://botid-${MicrosoftAppId}`
})
await context.sendActivity(MessageFactory.attachment(oauthCard))
so i get the token on the handler
handleTeamsSigninTokenExchange(context, query) {
if (context?.activity?.name === tokenExchangeOperationName) {
console.dir(context?.activity?.value)
token = context?.activity?.value?.token
}
}
What am I supposed to do with this token? I get the Invalid x5t claim error when I try to use microsoft client like this:
msGraphClient = microsoft.Client.init({
debugLogging: true,
authProvider: done => {
done(null, token)
}
})
// on message 'whoiam'
const me = await msGraphClient.api("me").get()
Is this the correct token? How do I initialize the Microsoft Graph client with this token?
My repo sample: https://github.com/itacirgabral/teamsbotSSOdemo/blob/nodialog/nodialogs.js

You can use below code snippet for initializing the Graph Client:
// Get an Authenticated Microsoft Graph client using the token issued to the user.
this.graphClient = Client.init({
authProvider: (done) => {
done(null, this._token); // First parameter takes an error if you can't get an access token.
}
});
Refence sample link:
https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/app-sso/nodejs/server/models/simpleGraphClient.js
https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/app-sso/nodejs/server/api/appController.js

Related

AuthenticationFailed when authenticating via nodejs app and package #azure/msal-node

I have an Azure app registered . I am trying to authenticate to that app . I am able to do that and successfully get the accesstoken and idtoken.
However, when I use that token and try to make a request to list subscriptions API (https://management.azure.com/subscriptions?api-version=2020-01-01) , the request fails and give response "AuthenticationFailed". I have also tried changing the scope to https://management.azure.com/.default but the same error is there. Below is the nodejs code and I am also attaching the API permissions of app
const config = {
auth: {
clientId: 'xxx',
authority: 'https://login.microsoftonline.com/organizations',
clientSecret: 'yyy',
},
system: {
loggerOptions: {
loggerCallback(loglevel, message, containsPii) {
console.log(message);
},
piiLoggingEnabled: false,
logLevel: msal.LogLevel.Verbose,
},
},
};
// Create msal application object
const pca = new msal.ConfidentialClientApplication(config);
// Create Express App and Routes
const app = express();
app.get('/', (req, res) => {
const authCodeUrlParameters = {
scopes: ['user.read','https://management.azure.com/user_impersonation'],
redirectUri: REDIRECT_URI,
};
// get url to sign user in and consent to scopes needed for application
pca
.getAuthCodeUrl(authCodeUrlParameters)
.then((response) => {
res.redirect(response);
})
.catch((error) => console.log(JSON.stringify(error)));
});
The response I am getting is
{
"error": {
"code": "AuthenticationFailed",
"message": "Authentication failed."
}
}
The error "AuthenticationFailed" usually occurs if you are using different scope token to call the API.
I tried to generate access token with the same scope as you
via Postman and got the same error while calling the query like below:
Please note that,
user.read audience is Microsoft Graph API
https://management.azure.com/user_impersonation audience is Azure Service Management.
As you have given two different scopes with different audiences, it will consider the first scope (user.read) to generate the token as mentioned in this SO Thread which was solved by me.
When you call the query https://management.azure.com/subscriptions?api-version=2020-01-01 with the above token, you will get the error as it is intended for MS Graph audience.
I tried to generate the token with scope https://management.azure.com/user_impersonation only, removing user.read like below:
With the above generated token, I am able to call the API successfully like below:
If you want token with different scopes, then you have to generate two access tokens separately.

Twitter oAuth using chrome extension

I am working on twitter oauth through chrome extension. I need to get oauth_token to authenticate the user. I am referring to https://developer.twitter.com/en/docs/tutorials/authenticating-with-twitter-api-for-enterprise/oauth1-0a-and-user-access-tokens. Can you guide me to send post request for my oauth token in javascript ?
You can refer to the above link for steps but I need to implement my post request in background.js instead to sending it in postman. I need my ext to create new request for each login, which for create different oauth token for each session.
I want to create a post request with following requirements:
URL-'https://api.twitter.com/oauth/request_token'
query- 'oauth_callback':'oob'
auth- we want to provide consumer key and consumer secret here
headers- 'Content-Type':'application/json'
This is a screenshot of postman. On implementing this, the post request returns oauth token and secret.
Please help me out on this.
import oauth from 'oauth';
const oauthCallback = process.env.FRONTEND_URL;
const CONSUMER_KEY = process.env.CONSUMER_KEY;
const CONSUMER_SECRET = process.env.CONSUMER_SECRET;
const _oauth = new oauth.OAuth(
'https://api.twitter.com/oauth/request_token',
'https://api.twitter.com/oauth/access_token',
CONSUMER_KEY, // consumer key
CONSUMER_SECRET, // consumer secret
'1.0',
oauthCallback,
'HMAC-SHA1',
);
export const getOAuthRequestToken = () => {
return new Promise((resolve, reject) => {
_oauth.getOAuthRequestToken((error, oauth_token, oauth_token_secret,
results) => {
if (error) {
reject(error);
} else {
console.log({ oauth_token, oauth_token_secret, results });
resolve({ oauth_token, oauth_token_secret, results });
}
});
});
};
Try this method in your backend to get the OAuth token and secret. It helped in my case, maybe it can work for you as well.
Use this to install oauth lib
npm i oauth
Refer for more info:
https://javascript.works-hub.com/learn/building-with-twitter-authentication-35ad6

How to verify Google signin (via Firebase) idToken in nodejs backend?

Trying to verify idToken of a user signed in via firebase authentication (Google signin) in nodejs server. Server throws Firebase ID token has invalid signature.
Tried verifying with firebase-admin as well as jsonwebtoken with public key from the url: https://www.googleapis.com/robot/v1/metadata/x509/securetoken#system.gserviceaccount.com. Both methods work perfect for users signed in with a password, but throws 'Invalid Signature' in case of a user signed in via google.
Is there anything I am doing wrong? Do I need to verify with google-auth-library instead?
Code:
import * as admin from "firebase-admin";
admin.initializeApp({
credential: admin.credential.cert(require("../../serviceAccount")), // file received from firebase project settings page
databaseURL: "as mentioned in the firebase project settings page",
});
// Some code here
var token = "token received from client side";
var decoded = await admin.auth().verifyIdToken(token);
PS:
All client side features (after signing in) are working fine.
Everything else on the backend is working fine.
Decoding the token in both cases gives expected JSON.
For test run, token is being forceRefreshed everytime before calling the API.
OP here,
I am dumb.
I was using the print() function of flutter to log the token and call the API myself. Didn't know Flutter's print function has an output character limit. Login using password gives smaller tokens thus the whole token was logged. But Google sign in gives a longer token, longer than the output character limit of print.
Solution : Use log function from 'dart:developer' package.
import 'dart:developer';
//
log(await _auth.idToken);
const { OAuth2Client } = require("google-auth-library");
const client = new OAuth2Client(googleClient[process.env.ENV])
let token = 123456789011-crhch2kuum79bk0qr3usa39f7b9chikc.apps.googleusercontent.com
async function googleLoginVerify(token) {
try {
const ticket = await client.verifyIdToken({
idToken: token,
audience: googleClient[process.env.ENV],
});
const payLoad = ticket.getPayload();
return {
success: true,
data: payLoad,
};
} catch (err) {
console.log(err.message);
return {
success: false,
message: err.message,
};
}
}

Auth0 "service not found" error

I'm attempting to use Auth0 to issue JWT tokens for accessing my API (so that Auth0 handles all the OAuth and security concerns, etc., and my API just needs to check the token). When I try to test the Authorization Code flow for clients to receive an access token (using Node + Express), the following happens:
The authorization code request works fine, and the client is redirected back to my redirect_uri with the code appended to the query. All good.
The token request then always fails. If I include the audience parameter, the request returns an access_denied error with the following details: Service not found: {the audience parameter}, regardless of what value I set for the audience parameter.
If I don't include the audience parameter, I get a server_error with the message Service not found: https://oauth.auth0.com/userinfo.
I've checked every Auth0 setting and read every documentation page thoroughly, and so far nothing has worked. I've also tested the Authorization Code flow in Auth0's API debugger, and it worked fine. My test follows exactly the same parameters, and yet still receives an error requesting the token. I'm testing on localhost. The client credentials and implicit flows are working fine.
Here is a test endpoint I created which retrieves the authorization code from Auth0:
const qs = require('querystring');
const getCode = (req, res) => {
const params = {
audience, // the value of the API Audience setting for the client
client_id, // the client ID
redirect_uri, // the redirect_uri, which is also listed in the Allowed Callback URLs field
response_type: `code`,
scope: `offline_access open` // ask to return ID token and refresh token,
state: `12345`,
};
const authDomain = `mydomain.auth0.com/oauth`;
res.redirect(`${authDomain}/oauth/authorize?${qs.stringify(params)}`);
};
The redirect_uri then redirects to the following endpoint, where I make the request for the access token:
const https = require('https');
const callback = (req, res) => {
const body = {
client_id,
client_secret,
code: req.query.code,
grant_type: `authorization_code`,
redirect_uri, // same value as provided during the code request
};
const opts = {
headers: { 'Content-Type': `application/json` },
hostname: `mydomain.auth0.com`,
method: `POST`,
path: `/oauth/token`,
};
const request = https.request(opts, response => {
let data = ``;
response.on(`data`, chunk => { data += chunk; });
response.on(`error`, res.send(err.message));
response.on(`end`, () => res.json(JSON.parse(data))); // this executes, but displays the error returned from Auth0
});
request.on(`error`, err => res.send(err.message));
request.end(JSON.stringify(body), `utf8`);
};
Any suggestions as to what I might be doing wrong?
The issue was that I was calling the incorrect URL at Auth0. I mistakenly thought that both the authorization and token endpoints began with /oauth, when in fact the authorization endpoint is just /authorize, while the token endpoint is /oauth/authorize. Correcting the URLs in my code fixed the problem.
My solution was the identifier of the api was not found. If it is not exact it won't find it. I had an extra backslash on my 'audience' where the identifier didnt have one. pretty easy mistake but the error is not very clear in Auth0.
In my case, I was using auth0 react hooks. So the example code looked like this:
const getUserMetadata = async () => {
const domain = process.env.REACT_APP_AUTH0_DOMAIN
try {
const accessToken = await getAccessTokenSilently({
audience: `https://${domain}/api/v2/`,
scope: 'read:current_user',
})
console.log('accessToken', accessToken)
localStorage.setItem('access_token', accessToken)
setUserAuthenticated(true)
} catch (e) {
console.log('error in getting access token', e.message)
}
}
My solution to this was using by default Auth0 Audience value in audience field
const getUserMetadata = async () => {
const auth0audience = process.env.REACT_APP_AUTH0_AUDIENCE
try {
const accessToken = await getAccessTokenSilently({
audience: auth0audience,
scope: 'read:current_user',
})
console.log('accessToken', accessToken)
localStorage.setItem('access_token', accessToken)
setUserAuthenticated(true)
} catch (e) {
console.log('error in getting access token', e.message)
}
}
Because its stated in auth0 docs of configuring custom domains that, you need to use by default API audience
Source - https://auth0.com/docs/brand-and-customize/custom-domains/configure-features-to-use-custom-domains

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

Resources