Next Auth Active Directory override profile with userinfo - azure

I am using Next Auth to authenticate through Azure Active Directory. I am successfully able to do so but the profile object does not contain some info I need.
I am trying to get the "user type" and "account status" properties.
Here's my code
providers: [
AzureADProvider({
clientId: process.env.AZURE_AD_CLIENT_ID,
clientSecret: process.env.AZURE_AD_CLIENT_SECRET,
tenantId: process.env.AZURE_AD_TENANT_ID,
userinfo: {
url: 'https://graph.microsoft.com/v1.0/me/',
params: {
scope: 'https://graph.microsoft.com/user.read',
grant_type: 'authorization_code'
},
},
})
]
I don't know what to do after this point or even if this is what I should do. Any help is appreciated.
UPDATE:
Here's what I have after changing to what was suggested
providers: [
AzureADProvider({
clientId: process.env.AZURE_AD_CLIENT_ID,
clientSecret: process.env.AZURE_AD_CLIENT_SECRET,
tenantId: process.env.AZURE_AD_TENANT_ID,
userinfo: {
url: 'https://graph.microsoft.com/v1.0/me?$select=accountEnabled,userType,displayName,givenName,objectId,email,surname',
params: {
scope: 'https://graph.microsoft.com/user.read',
grant_type: 'authorization_code',
},
},
profile(profile) {
return {
id: profile.objectId,
name: profile.displayName,
lastName: profile.surname,
firstName: profile.givenName,
email: profile.email,
userType: profile.userType,
accountStatus: profile.accountEnabled
};
}
})]
It seems like the profile data from the AzureADProvider is still being used because of the id token. I thought userinfo would overwrite it but it doesn't seem to work that way unless I am doing it wrong.

I tried to reproduce the same in my environment and got the results like below:
I created Azure AD Application and granted API permissions:
I generated the Access Token using Authorization Code Flow by using parameters like below:
GET https://login.microsoftonline.com/TenantId/oauth2/v2.0/token
client_id:ClientID
client_secret:ClientSecret
scope:https://graph.microsoft.com/user.read
grant_type:authorization_code
redirect_uri:RedirectUri
code:code
When I ran the same query as you, I dint get the userType and account status properties like below:
GET https://graph.microsoft.com/v1.0/me
Note that : By default only businessPhones, displayName, givenName, id, jobTitle, mail, mobilePhone, officeLocation, preferredLanguage, surname, userPrincipalName properties will be returned.
To get the additional user properties, make use of $select like below:
GET https://graph.microsoft.com/v1.0/me?$select=accountEnabled,userType
Modify the code like below:
providers: [
AzureADProvider({
clientId: process.env.AZURE_AD_CLIENT_ID,
clientSecret: process.env.AZURE_AD_CLIENT_SECRET,
tenantId: process.env.AZURE_AD_TENANT_ID,
userinfo: {
url: 'https://graph.microsoft.com/v1.0/me?$select=accountEnabled,userType',
params: {
scope: 'https://graph.microsoft.com/user.read',
grant_type: 'authorization_code'
},
},
})
]

I found a solution. I had to use the request function inside userinfo and fetch the profile data.
AzureADProvider({
clientId: process.env.AZURE_AD_CLIENT_ID,
clientSecret: process.env.AZURE_AD_CLIENT_SECRET,
tenantId: process.env.AZURE_AD_TENANT_ID,
userinfo: {
url: 'https://graph.microsoft.com/v1.0/me?$select=id,userPrincipalName,accountEnabled,userType,givenName,surname',
async request(context) {
const response = await axios.get('https://graph.microsoft.com/v1.0/me?$select=id,userPrincipalName,accountEnabled,userType,givenName,surname',
{
headers: {
'Authorization': `Bearer ${context.tokens.access_token}`
}
}
)
const newProfile = await response.data
return {
id: newProfile.id,
email: newProfile.userPrincipalName,
firstName: newProfile.givenName,
lastName: newProfile.surname,
userType: newProfile.userType,
accountStatus: newProfile.accountEnabled
};
}
},
profile(userinfo) {
console.log(userinfo)
return {
id: userinfo.id,
email: userinfo.userPrincipalName,
firstName: userinfo.givenName,
lastName: userinfo.surname,
userType: userinfo.userType,
accountStatus: userinfo.accountEnabled
};
}
}),

Related

Implement OAuth Implicit Flow in NestJS Swagger

Using the Swagger module provided from NestJS there is no functionality to implement an OAuth connection that automatically get the user token through implicit flows.
From the website there is a lack of documentation about oauth.
https://docs.nestjs.com/openapi/security.
The solution that I've found is using this piece of code
const config = new DocumentBuilder()
.setTitle('Swagger API')
.setVersion('1.0')
.addSecurity('ApiKeyAuth', {
type: 'apiKey',
in: 'header',
name: 'token',
})
.addBearerAuth()
.addOAuth2(
{
type: 'oauth2',
flows: {
implicit: {
tokenUrl: `${configSv.get("OAUTH_DOMAIN")}/oauth/token`,
authorizationUrl: `${configSv.get("OAUTH_DOMAIN")}/authorize`,
scopes: {"read:products": null, "read:properties": null, "read:categories": null, openid: null, profile: null, email: null},
},
},
},
)
.build();
But doesn't retrieve correctly the token and scopes aren't working, facing a 403 error calling the endpoints

How to push transaction EOS Blockchain (eosjs - unsatisfied_authorization)?

I use Anchor Wallet.
This is init code
const privateKeys = ["myprivatekey"];
const signatureProvider = new JsSignatureProvider(privateKeys);
const rpc = new JsonRpc(config.mainnet, { fetch });
const api = new Api({ rpc, signatureProvider, textDecoder: new TextDecoder(), textEncoder: new TextEncoder() });
return {rpc, api};
Then i try to push transaction
const transaction = await api.transact({
actions: [{
account: 'eosio',
name: 'buyrambytes',
authorization: [{
actor: 'username',
permission: 'active',
}],
data: {
payer: 'username',
receiver: 'username',
bytes: 8192,
},
}]
}, {
blocksBehind: 3,
expireSeconds: 30,
});
(docs example)
and receive error
details: [
{
message: `transaction declares authority '{"actor":"username","permission":"active"}', but does not have signatures for it.`,
file: 'authorization_manager.cpp',
line_number: 643,
method: 'get_required_keys'
}
],
json: {
code: 401,
message: 'UnAuthorized',
error: {
code: 3090003,
name: 'unsatisfied_authorization',
what: 'Provided keys, permissions, and delays do not satisfy declared authorizations',
details: [Array]
}
}
}
Maybe i enter wrong private key (Anchor -> Export private key -> Copy key) or something else, idk.
All other functions (for exampe get_block etc.) works fine
try with owner:
authorization: [{
actor: 'username',
permission: 'owner',
}],

How to get user information using node oidc provider

I got access token and I will pass access_token to userinfo endpoint it throwing an invalid token provided error How to fix this Issue. I tried to debug why this error is throwing we have validateAccessToken method(userinfo.js) in this method check the access_token is exist or not exist using this code await ctx.oidc.provider.AccessToken.find(accessTokenValue); when i print this result it's showing undefined
oidc_configuration.js
const oidc = new Provider('http://localhost:3000', {
clients: [
{
client_id: 'oidcCLIENT',
client_secret: '...',
grant_types: ['refresh_token', 'authorization_code'],
redirect_uris: ['http://sso-client.dev/providers/7/open_id', 'http://sso-client.dev/providers/8/open_id'],
}
],
interactions: {
url(ctx, interaction) { // eslint-disable-line no-unused-vars
return `/api/v1/open_id/interaction/${interaction.uid}`;
},
},
cookies: {
keys: ['some secret key', 'and also the old rotated away some time ago', 'and one more'],
},
claims: {
address: ['address'],
email: ['email', 'email_verified'],
phone: ['phone_number', 'phone_number_verified'],
profile: ['birthdate', 'family_name', 'gender', 'given_name', 'locale', 'middle_name', 'name',
'nickname', 'picture', 'preferred_username', 'profile', 'updated_at', 'website', 'zoneinfo'],
},
features: {
devInteractions: { enabled: false }, // defaults to true
deviceFlow: { enabled: true }, // defaults to false
revocation: { enabled: true }, // defaults to false
},
jwks: {
keys: [
{
d: 'VEZOsY07JTFzGTqv6cC2Y32vsfChind2I_TTuvV225_-0zrSej3XLRg8iE_u0-3GSgiGi4WImmTwmEgLo4Qp3uEcxCYbt4NMJC7fwT2i3dfRZjtZ4yJwFl0SIj8TgfQ8ptwZbFZUlcHGXZIr4nL8GXyQT0CK8wy4COfmymHrrUoyfZA154ql_OsoiupSUCRcKVvZj2JHL2KILsq_sh_l7g2dqAN8D7jYfJ58MkqlknBMa2-zi5I0-1JUOwztVNml_zGrp27UbEU60RqV3GHjoqwI6m01U7K0a8Q_SQAKYGqgepbAYOA-P4_TLl5KC4-WWBZu_rVfwgSENwWNEhw8oQ',
dp: 'E1Y-SN4bQqX7kP-bNgZ_gEv-pixJ5F_EGocHKfS56jtzRqQdTurrk4jIVpI-ZITA88lWAHxjD-OaoJUh9Jupd_lwD5Si80PyVxOMI2xaGQiF0lbKJfD38Sh8frRpgelZVaK_gm834B6SLfxKdNsP04DsJqGKktODF_fZeaGFPH0',
dq: 'F90JPxevQYOlAgEH0TUt1-3_hyxY6cfPRU2HQBaahyWrtCWpaOzenKZnvGFZdg-BuLVKjCchq3G_70OLE-XDP_ol0UTJmDTT-WyuJQdEMpt_WFF9yJGoeIu8yohfeLatU-67ukjghJ0s9CBzNE_LrGEV6Cup3FXywpSYZAV3iqc',
e: 'AQAB',
kty: 'RSA',
n: 'xwQ72P9z9OYshiQ-ntDYaPnnfwG6u9JAdLMZ5o0dmjlcyrvwQRdoFIKPnO65Q8mh6F_LDSxjxa2Yzo_wdjhbPZLjfUJXgCzm54cClXzT5twzo7lzoAfaJlkTsoZc2HFWqmcri0BuzmTFLZx2Q7wYBm0pXHmQKF0V-C1O6NWfd4mfBhbM-I1tHYSpAMgarSm22WDMDx-WWI7TEzy2QhaBVaENW9BKaKkJklocAZCxk18WhR0fckIGiWiSM5FcU1PY2jfGsTmX505Ub7P5Dz75Ygqrutd5tFrcqyPAtPTFDk8X1InxkkUwpP3nFU5o50DGhwQolGYKPGtQ-ZtmbOfcWQ',
p: '5wC6nY6Ev5FqcLPCqn9fC6R9KUuBej6NaAVOKW7GXiOJAq2WrileGKfMc9kIny20zW3uWkRLm-O-3Yzze1zFpxmqvsvCxZ5ERVZ6leiNXSu3tez71ZZwp0O9gys4knjrI-9w46l_vFuRtjL6XEeFfHEZFaNJpz-lcnb3w0okrbM',
q: '3I1qeEDslZFB8iNfpKAdWtz_Wzm6-jayT_V6aIvhvMj5mnU-Xpj75zLPQSGa9wunMlOoZW9w1wDO1FVuDhwzeOJaTm-Ds0MezeC4U6nVGyyDHb4CUA3ml2tzt4yLrqGYMT7XbADSvuWYADHw79OFjEi4T3s3tJymhaBvy1ulv8M',
qi: 'wSbXte9PcPtr788e713KHQ4waE26CzoXx-JNOgN0iqJMN6C4_XJEX-cSvCZDf4rh7xpXN6SGLVd5ibIyDJi7bbi5EQ5AXjazPbLBjRthcGXsIuZ3AtQyR0CEWNSdM7EyM5TRdyZQ9kftfz9nI03guW3iKKASETqX2vh0Z8XRjyU',
use: 'sig',
}, {
crv: 'P-256',
d: 'K9xfPv773dZR22TVUB80xouzdF7qCg5cWjPjkHyv7Ws',
kty: 'EC',
use: 'sig',
x: 'FWZ9rSkLt6Dx9E3pxLybhdM6xgR5obGsj5_pqmnz5J4',
y: '_n8G69C-A2Xl4xUW2lF0i8ZGZnk_KPYrhv4GbTGu5G4',
},
],
},
});
// Heroku has a proxy in front that terminates ssl, you should trust the proxy.
oidc.proxy = true;
const callback = oidc.callback();
How to fix this issue
You're running without a persistent adapter, meaning an in-memory one is used, are you possibly restarting your server after receiving the access token before calling the userinfo endpoint?
After completing the authentication, you will get access_token as well as id_token. I think you were using id_token in place of access_token which is why you are seeing that error
Instead use access_token then you can see the details

Docusign Text Tabs in template not populating

I am using docusign (production account) to allow users to sign documents. I am trying to add data to textTabs that I create through the docusign dashboard. So say if I add a text box and call it nbShares, the below code does not populate the box. All the boxes I add in the dashboard such as signature, text, checkboxes etc are not shown in the generated link. I get no API errors either. I also tried to create customFields, and pass data to them as textTab - however this did not work either.
I think I may misunderstand the flow, I add all recipients and signers programatically - is that why I cant see the placeholders/buttons I add? I have also allowed collaboration in the fields I add, made them mandatory - yet they still do not appear.
Would really appreciate some help on this. This is my envelope definition - Im using the node sdk along with ts types.
const makeEnvelopeDefinition = async (
templateName: string,
user: User,
dealId?: string,
): Promise<EnvelopeDefinition> => {
const personToSign: Signer = {
email: user.email,
name: user.name,
roleName: 'Signer',
// Should this work?
tabs: {
textTabs: [
{ tabLabel: 'nbShares', value: '1000' },
],
},
clientUserId: DOCUSIGN_CLIENT_USER_ID,
recipientId: '1',
}
const compositeTemplate: CompositeTemplate = {
serverTemplates: [
{ sequence: '1', templateId: 'remote-template-id' },
],
inlineTemplates: [
{
sequence: '1',
recipients: {
signers: [personToSign],
certifiedDeliveries: [
{
email: 'someemail#something.com',
recipientId: '77',
name: 'Receipt of transaction',
},
{
email: user.email,
recipientId: '771',
name: user.name,
},
],
},
},
],
}
// create the envelope definition
const envelope: EnvelopeDefinition = {
emailSubject: 'Review signed document',
status: 'sent',
compositeTemplates: [compositeTemplate],
}
return envelope
}
For me the solution was changing my signer to this
const personToSign: Signer = {
email: user.email,
name: user.name,
clientUserId: await getDocusignConfig().DOCUSIGN_CLIENT_USER_ID,
recipientId: '1',
routingOrder: '1', // Was missing this
roleName: 'Signer', // Must match signer specified on docusign dashboard
tabs: {
textTabs: [
{
xPosition: '150',
yPosition: '200',
name: 'First Name',
tabLabel: 'name', // Must match the dataLabel name specified on docusign
value: 'Test',
},
],
},
}
And enabling this setting 'When an envelope is sent, write the initial value of the field for all recipients' in 'Signing Settings' on docusign.

Amazon Cognito - AdminCreateUser - how to not skip email verification

I am using Amazon Cognito user pool and AdminCreateUser api to create a new user so that we don't allow users to sign themselves up. It works great but it seems that email verification step is being skipped so when making an api call, I needed to set email_verified attribute to true to make reset password flow to work.
Can I make email verification to happen before sending out an inviatation email?
const params = {
DesiredDeliveryMediums: ['EMAIL'],
UserAttributes: [
{
Name: 'email',
Value: email
},
{
Name: 'email_verified',
Value: 'True'
},
],
Username: email,
UserPoolId: userPoolId,
}
cognitoIdentityService.adminCreateUser(params, function(err, data) {
// ...
To prevent backend to send verification email, set MessageACtion="SUPPRESS"
response = client.admin_create_user(
UserPoolId='USER_POOL_ID',
Username='USERNAME',
TemporaryPassword='PASSWORD',
UserAttributes=[
{
'Name': 'email',
'Value': 'email#example.com'
},
{
'Name': 'email_verified',
'Value': 'true'
}
],
MessageAction='SUPPRESS'
)

Resources