ValidationError: "tenantId" is not allowed when using AzureAD for hapi JS route authorization - azure

We are trying to use #hapi/bell on our back-end routes to provide authorization. The authentication strategy uses azure as provider and the scheme is bell
This is how I register the strategy. The clientId, clientSecret, tenantId and password are hidden for obvious reasons
server.auth.strategy('azureAD', 'bell', {
provider: 'azure',
clientId: '...',
clientSecret: '...',
tenantId: '...',
password: '...',
providerParams: {
response_type: 'code'
},
scope: ['openid', 'offline_access', 'profile', 'User.Read']
})
When I run the server, I get the following error:
{ [ValidationError: "tenantId" is not allowed] ...
Now, looking into the azure portal, we definitely want to be supporting accounts only inside the organisation i.e. single-tenant.
If I remove the tenantId option and restart the server I get CORS error which essentially says that our app is not configured as a multi-tenant application and we need to use a tenant-specific endpoint or configure the application to be multi-tenant. Adding the tenantId, however, says that it is not allowed.
Any guidance as to why this is happening will be highly appreciated.

I found out that instead of registering the strategy as I have shown in the question the following could be done:
const custom = Bell.providers.azure({ tenant: '...' })
server.auth.strategy('azureAD', 'bell', {
provider: custom,
clientId: '...',
clientSecret: '...',
password: '...',
isSecure: false, // look into this, not a good idea but required if not using HTTPS
providerParams: {
response_type: 'code'
},
scope: ['openid', 'offline_access', 'profile', 'User.Read']
})
This gets rid of the "tenantId" is not allowed error, however, we now get a different error stating Authentication failed due to: Missing custom request token cookie.
Bell suggests that a common solution is to combine bell with the hapi-auth-cookie authentication scheme plugin, so now this is something to look into.

The Joi package is validating the schema and throwing errors. See config property below. It overrides options and avoids the "tenantId" or "tenant" is not allowed error you are seeing. Also, in recent versions Bell Azure Provider wants "tenant" not "tenantId" property.
server.auth.strategy('azureAD', 'bell', {
provider: 'azure',
config: {
tenant: '...',
useParamsAuth: false,
},
clientId: '...',
clientSecret: '...',
password: '...',
providerParams: {
response_type: 'code'
},
scope: ['openid', 'offline_access', 'profile', 'User.Read'],
isSecure: false,
})

Related

AUTHENTICATE failed: Oauth2 Node Office 365 Microsoft

I have following code which is supposed to open a connection to a Microsoft Exchange server via IMAP and read Emails.
{
user: '****',
password: '****'
xoauth2= Buffer.from([`user=*****`, `auth=Bearer **MyToken**`, '', ''].join('\x01'), 'utf-8').toString('base64'),
host: 'outlook.office365.com',
port: 993,
tls: true,
autotls: 'always',
tlsOptions: { rejectUnauthorized: false, servername: 'outlook.office365.com' },
socketTimeout: 10000,
keepalive: { interval: 30000, idleInterval: 300000, forceNoop: false },
debug: false
}
This is how I get Token
const msal = require('#azure/msal-node');
const config = {
auth: {
clientId: "ClientID",
// authority: "https://login.microsoftonline.com/common/",
authority: "https://login.microsoftonline.com/Tenant",
clientSecret: "ClientSecret"
}
};
// Create msal application object
const cca = new msal.ConfidentialClientApplication(config);
// With client credentials flows permissions need to be granted in the portal by a tenant administrator.
// The scope is always in the format "<resource>/.default"
const clientCredentialRequest = {
scopes: ["https://outlook.office365.com/.default"], // replace with your resource
// scopes: ["https://graph.microsoft.com/.default","https://outlook.office.com/IMAP.AccessAsUser.All"]
};
const getToken = async () => {
return cca.acquireTokenByClientCredential(clientCredentialRequest);
}
module.exports={
getToken
}
This is granted privileges on Azure
But finally I have this error
[connection] Connected to host
<= '* OK The Microsoft Exchange IMAP4 service is ready. [TQBSADIAUAAyADYANABDAEEAMAAxADMAMwAuAEYAUgBBAFAAMgA2ADQALgBQAFIATwBEAC4ATwBVAFQATABPAE8ASwAuAEMATwBNAA==]'
=> 'A0 CAPABILITY'
<= '* CAPABILITY IMAP4 IMAP4rev1 AUTH=PLAIN AUTH=XOAUTH2 SASL-IR UIDPLUS ID UNSELECT CHILDREN IDLE NAMESPACE LITERAL+'
<= 'A0 OK CAPABILITY completed.'
=> 'A1 AUTHENTICATE XOAUTH2 dXNlchCYU9HVXRkVEZTZVhCTWNWaHpja3RNYVVsTWVYcDRlR2ROUWpSVVVUazRNVlpTTmtock1YTWlMQ0poYkdjaU9pSlNVekkxTmlJc0luZzFkQ0k2***=='
<= 'A1 NO AUTHENTICATE failed.'
Error: AUTHENTICATE failed.
That error usually indicates that you haven't registered the service principal or granted explicit access to the Mailbox this is at the bottom of https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth#register-service-principals-in-exchange
IMAP App permissions work differently from the Graph App permission in the Graph when you have App permission you get access to everybody's mailbox in the tenant by default. With the IMAP permission you don't and every mailbox that you need to access will need to have permission explicitly granted via Add-Mailbox (and you need to wait 15-60 minutes for those permissions to take affect)

How to connect to Gmail using node.js imapflow?

I'm using imapflow package like this:
import config from './config.js';
import { ImapFlow } from 'imapflow';
const client = new ImapFlow({
host: 'imap.gmail.com',
port: 993,
secure: true,
auth: {
user: 'muhemail#gmail.com',
pass: '123'
}
});
await client.connect();
console.log('OK');
And it throws with Invalid credentials (Failure). I've quadruple-checked that login and password are correct, IMAP is enabled in GMail settings. Less than secure apps aren't enabled though, and I'd prefer to keep it that way. When I try to use the same credentials in Thunderbird, it opens an OAuth login window, which I suppose I should somehow incorporate with imapflow? Or is there a different solution?
Gmail does not allow accessing its IMAP services by using plain username and password.
You should first get OAuth2.0 credentials via the Gmail api example (here) and then should convert it to xoauth2.
let {installed: {client_id, client_secret}} = require('./client_secret') // the client_secret.json file
let xoauth2gen = xoauth2.createXOAuth2Generator({
user: '*******', // the email address
clientId: client_id,
clientSecret: client_secret,
refreshToken: refresh_token
})
xoauth2gen.getToken(function(err, xoauth2token) {
if (err) {
return console.log(err)
}
let imap = new Imap({
xoauth2: xoauth2token,
host: 'imap.gmail.com',
port: 993,
tls: true,
authTimeout: 10000,
debug: console.log,
})

TS - NodeMailer OAuth2 'auth' does not exist in type 'TransportOptions

I have a problem. I need to make email sending function with Gmail api. In docs it tells me to make a createTransport function like this:
const transport = nodemailer.createTransport({
service: "gmail",
auth: {
type: "OAuth2",
user: "horvatlucian1#gmail.com",
clientId: CLIENT_ID,
clientSecret: CLIENT_SECRET,
refreshToken: REFRESH_TOKEN,
accessToken: accessToken,
},
})
DOCS
But it all turns red and returns:
Error
Any solution? Thanks.
There is a problem of type script when you tring to pass the accessToken because in google oauth2client the return type of getAccessToken() is https://github.com/googleapis/google-auth-library-nodejs/blob/b0ddb7512fb9ed1e51b6874b7376d7e1f26be644/src/auth/oauth2client.ts#L331
that not supported by the types of nodemailer that should be string:
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/7827d853a1d623dd5345d9a11ced3e15eb5d3d8b/types/nodemailer/lib/xoauth2.d.ts#L24
by looking at the return type of the getAccessToken you can see that the token is inside that have type string so to fix that just destructure your token from the accessToken.
const transport = nodemailer.createTransport({
service: "gmail",
auth: {
type: "OAuth2",
user: "horvatlucian1#gmail.com",
clientId: CLIENT_ID,
clientSecret: CLIENT_SECRET,
refreshToken: REFRESH_TOKEN,
accessToken: accessToken.token,
},
})
1/
const transport = nodemailer.createTransport({
service: String("gmail") || "",
auth: {
type: String("OAuth2"),
user: String(SENDER_MAIL),
clientId: String(CLIENT_ID),
clientSecret: String(CLIENT_SECRET),
refreshToken: String(REFRESH_TOKEN),
access_token: String(access_token),
},
});
and the ERRO WITH MSG :
[ERROR] 14:02:23 тип Unable to compile TypeScript:
server/config/sendmail.ts(25,7): error TS2769: No overload matches this call.
The last overload gave the following error.
Argument of type '{ service: string; auth: { type: string; user: string; clientId:string; clientSecret:string; refreshToken:string; access_token:GetAccessTokenResponse; }; }' is not assignable to parameter of type 'TransportOptions | Transport<unknown>'.
Object literal may only specify known properties, and 'service' does not exist in type 'TransportOptions | Transport<unknown>'.
The accessToken required in nodemailer.createTransport is nullable
Because of that, to avoid error, you must cover the case when accessToken is null by providing an empty string or something similar to avoid null error
const transport = nodemailer.createTransport({
service: 'gmail',
auth: {
type: 'OAuth2',
user: process.env.EMAIL_ADDRESS,
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
refreshToken: process.env.REFRESH_TOKEN,
accessToken: accessToken.token || '',
},
})
Hopefully that help

Cannot read outlook profile using passport-outlook strategy with nestjs

Always getting the following error while using passport-outlook strategy with scopes scope: ['openid', 'profile', 'offline_access']
In the nest middleware,
consumer.apply(authenticate('outlook', {
session: false,
accessType: 'offline',
scope: ['openid', 'profile', 'offline_access'],
callbackURL: `${callbackUrl}/auth/outlook/login/callback`,
})).forRoutes(
{ path: '/auth/outlook/login', method: RequestMethod.GET },
{ path: '/auth/outlook/login/callback', method: RequestMethod.GET },
)
}
{"error":{"code":"InvalidMsaTicket","message":"ErrorCode:
'PP_E_RPS_CERT_NOT_FOUND'. Message: ' Internal error:
spRPSTicket->ProcessToken failed. Failed to call
CRPSDataCryptImpl::UnpackData: Internal error: Failed to decrypt data.
:Failed to get session key. RecipientId=293577. spCache->GetCacheItem
returns error.:Cert Name: (null). SKI:
45237f1479435b9c4def8b7a1b36edb0105e0546...'","innerError":{"requestId":"4ea03cf1-79f8-421f-a0eb-d5bc6560da6a","date":"2019-07-05T08:44:20"}}}
Did I missed anything in the azure app registration config ?
Do you set up passport.use(new OutlookStrategy({})) in main function with providing there clientID and clientSecret?

how to properly configure mean.js passport to authenticate with twitter

In my mean.js based app, I'm trying to implement passport twitter authentication.
My twitter section in development.js looks like this:
twitter: {
clientID: process.env.TWITTER_KEY || ' somekey1234',
clientSecret: process.env.TWITTER_SECRET || 'someSecret1234',
callbackURL: '/auth/twitter/callback'
}
I have signed up with twitter, added my mobile number to my profile, created an app, and got the consumer key and consumer secret.
My Website entry in twitter looks like this:
http://10.211.55.25:3000
My callback url entry looks like this:
http://10.211.55.25:3000/auth/twitter/callback
When I attempt to "Sign up using Twitter" in my app. I get this error:
Error: {"errors":[{"code":32,"message":"Could not authenticate you."}]}
at Strategy.parseErrorResponse (/home/eugene/dev/node/DataManager_0.2/node_modules/passport-twitter/lib/strategy.js:182:10)
at Strategy.OAuthStrategy._createOAuthError (/home/eugene/dev/node/DataManager_0.2/node_modules/passport-twitter/node_modules/passport-oauth1/lib/strategy.js:349:16)
at OAuthStrategy.authenticate (/home/eugene/dev/node/DataManager_0.2/node_modules/passport-twitter/node_modules/passport-oauth1/lib/strategy.js:218:41)
at exports.OAuth.getOAuthRequestToken (/home/eugene/dev/node/DataManager_0.2/node_modules/passport-twitter/node_modules/passport-oauth1/node_modules/oauth/lib/oauth.js:543:17)
at exports.OAuth._performSecureRequest.passBackControl (/home/eugene/dev/node/DataManager_0.2/node_modules/passport-twitter/node_modules/passport-oauth1/node_modules/oauth/lib/oauth.js:397:13)
at IncomingMessage.exports.OAuth._performSecureRequest.request.on.callbackCalled (/home/eugene/dev/node/DataManager_0.2/node_modules/passport-twitter/node_modules/passport-oauth1/node_modules/oauth/lib/oauth.js:409:9)
at IncomingMessage.EventEmitter.emit (events.js:117:20)
at _stream_readable.js:920:16
at process._tickCallback (node.js:415:13)
As I'm getting this error, the app url in my browser is being set to:
http://10.211.55.25:3000/auth/twitter
What can I do to fix this?
You have an error in the JSON values (An space that is present in TWITTER_KEY value):
twitter: {
clientID: process.env.TWITTER_KEY || ' somekey1234',
clientSecret: process.env.TWITTER_SECRET || 'someSecret1234',
callbackURL: '/auth/twitter/callback'
}
should be
twitter: {
clientID: process.env.TWITTER_KEY || 'somekey1234',
clientSecret: process.env.TWITTER_SECRET || 'someSecret1234',
callbackURL: '/auth/twitter/callback'
}

Resources