Set clientId in swagger ui nestjs oauth2 - javascript

The ultimate goal (which works if clientId is provided and scopes are clicked): Use Swagger UI to get the azure Auth to receive an accessToken for further requests.
Since the client_id and scopes are static I was hoping to bypass the popup and immediately trigger what happens when clicked on the Authorize button by pre setting the client_id and scopes, since I couldn't find anything there I am atleast trying to pre fill the form so the user only has to click Authorize in my organisation.
What I tried without success:
swagger options initOAuth
DocumentBuilder.components.requestBodies
The Code in main.ts of nestjs:
// Swagger
const config = new DocumentBuilder()
.setTitle('Auth Backend')
.setDescription('Azure PoC backend')
.setVersion('0.1')
.addTag('auth')
.addOAuth2({
type: "oauth2",
description: "description",
name: "AzureAD",
flows: {
implicit: {
scopes: { "User.Read": "Read user profile" },
authorizationUrl: `https://login.microsoftonline.com/${process.env.TENANT_ID}/oauth2/v2.0/authorize`,
}
}
}, "AzureAD")
.build()
const document = SwaggerModule.createDocument(app, config)
SwaggerModule.setup('swagger', app, document, {initOAuth: {clientId: process.env.CLIENT_ID, clientSecret: process.env.CLIENT_SECRET}});

Please try by including swaggerOptions in SwaggerModule.setup which can pass swaggerOptions into swaggerUi.generateHTML
SwaggerModule.setup('api', app, document, { customSiteTitle: 'Your API name', swaggerOptions: {
oauth: {
clientId: clientid",
clientSecret: "clientsecret",
realm: "your-realms",
appName: " ",
scopeSeparator: " ",
scopes: ["User.Read", "profile",”offline_access”],
…. },
persistAuthorization: true, }, });
For the latest versions: ( as given by #julianklumpers in Access swagger-ui after setup to initialize oauth2 -nest.js· Issue · GitHub)
SwaggerModule.setup('api', app, document, {
customSiteTitle: 'API',
swaggerOptions: {
persistAuthorization: true,
oauth2RedirectUrl: 'https://…….’,
initOAuth: {
ClientId,
ClientSecret,
scopes: ["User.Read", "profile",”offline_access”],
appName: ‘name of the app',
},
},
});
Reference: swagger-ui oauth2 · GitHub

Related

Nuxtjs auth module - token endpoint in auth strategies configuration is never called

We have two endpoints /auth and /token. The endpoint /auth returns the authorization code that can be used when calling /token to get an access token.
Using NuxtJS the auth module became the way to go. The login process this.$auth.loginWith("company") is working fine as far as I know. I get redirected to the Login Page. I can enter my credentials and when those are valid I get redirected to the configured URL.
Up to that point everything works as expected. The redirect is passing the authorization code as request parameters.
This is what the URL looks like:
http://localhost:3000/?state=Y6CWcCZanJ&session_state=2c966cd9-5834-4045-9bfb-6aa9f616f841&code=fbabf615-cd5e-4479-818a-6a7ba72de01b.2c966cd9-5834-4045-9bfb-6aa9f616f841.553d562b-c454-4681-83ae-98cd93dbfa90
However with this code I expect that the auth module is automatically calling the /token endpoint. But it does not. Why is that?
Do I need to call it explicitly after using this.$auth.loginWith("company")? Something like:
this.$auth.loginWith("company");
this.$auth.fetchToken();
Or is it done implictly?
This is the configuration in nuxt.config.js
...
auth: {
strategies: {
company: {
scheme: "oauth2",
endpoints: {
authorization:
"https://login.mycompany.com/auth/realms/apps/protocol/openid-connect/auth",
token:
"https://login.mycompany.com/auth/realms/apps/protocol/openid-connect/token",
userInfo:
"https://login.mycompany.com/auth/realms/apps/protocol/openid-connect/userinfo",
logout: "http://localhost:3000/logout"
},
token: {
name: "Authorization",
property: "access_token",
type: "Bearer",
maxAge: 1800
},
refreshToken: {
property: "refresh_token",
maxAge: 60 * 60 * 24 * 30
},
responseType: "code",
grantType: "authorization_code",
accessType: undefined,
redirectUri: "http://localhost:3000",
logoutRedirectUri: undefined,
clientId:
process.env.CLIENT_ID ||
"3004761-241-dab74c5e-ad70-11eb-bea4-4193bd361dc612123",
scope: ["all"],
codeChallengeMethod: "S256"
}
}
},
...
Is there any chance you forgot to setup #nuxtjs/axios or explicitly configure auth middleware?
// nuxt.config.js
modules: ['#nuxtjs/auth-next', '#nuxtjs/axios'],
router: {
middleware: ['auth'],
},

nuxt auth : Google provider return invalid_request

I try to set google authentication with Nuxt Auth module, but I got following error from google:
Error 400 : invalid_request
Parameter not allowed for this message type: nonce
Here is my config
// nuxt.config.js
modules: ["#nuxtjs/axios", "#nuxtjs/auth-next"],
auth: {
router: {
middleware: ["auth"],
},
redirect: {
login: "/login",
logout: "/",
callback: false,
home: "/",
},
strategies: {
google: { clientId: "MyClientID", codeChallengeMethod: "" }
}
}
And how i call the google auth in my component:
login(){
this.$auth.loginWith("google").then( result => console.log(result) )
}
I also try to run official demo from here:
https://github.com/nuxt-community/auth-module/tree/dev/demo
But I got the same error.
Had the same issue but setting responseType: 'code' worked for me.
EDIT: Set responseType: "id_token token" for an implicit grant flow and to get redirected to your Nuxt app with an access token by Google. This whole OAuth topic has always been quite confusing to me and there's so much misinformation out there on what to do and what not to do in terms of security. I have found the following article to be very helpful and demystify the various OAuth flows: https://itnext.io/an-oauth-2-0-introduction-for-beginners-6e386b19f7a9 However if you do not want to expose the access token on the front end and rather obtain it in your back end then you must use the code grant flow by setting responseType: "code" and set up your back end properly. I ended up using the code grant flow but I assume that most people find the implicit grant flow more convenient.
Here's the full snippet:
export default {
modules: ["#nuxtjs/axios", "#nuxtjs/auth-next"],
auth: {
strategies: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID,
redirectUri: `${process.env.BASE_URL}/auth/google/callback`,
codeChallengeMethod: "",
responseType: "id_token token",
},
},
},
router: {
middleware: ["auth"],
},
};
Seems to be related with the version of Nuxt Auth.
Maybe this feature is not ready in #nuxtjs/auth-next
So i run
npm install #nuxtjs/auth#latest --save
Then update nuxt.config.json
replace #nuxtjs/auth-next with #nuxtjs/auth
replace clientId with client_id
// nuxt.config.js
modules: ["#nuxtjs/axios", "#nuxtjs/auth"],
auth: {
router: {
middleware: ["auth"],
},
redirect: {
login: "/login",
logout: "/",
callback: false,
home: "/",
},
strategies: {
google: { client_id: "MyClientID"}
}
}
in auth-next and auth0 the setting of responseType let me bypass the problem, my working config looks like:
auth0: {
logoutRedirectUri: process.env.BASE_URL,
domain: String(process.env.AUTH0_DOMAIN).replace(/(^\w+:|^)\/\//, ''),
clientId: process.env.AUTH0_CLIENT_ID,
scope: ['read:reports', 'read:roles', 'create:client_grants', 'offline_access'], // 'openid', 'profile', 'email' default added
audience: process.env.AUTH0_AUDIENCE,
responseType: 'token',
accessType: 'offline',
grantType: 'client_credentials',
redirectUri: process.env.BASE_URL + '/callback',
codeChallengeMethod: 'S256'
}

Azure OAuth with Cypress: Infinite Loop

Trying to set up Cypress to test an application that uses OAuth against Azure AD. My login command is defined as follows:
Cypress.Commands.add('login', () => {
return cy.request('POST', Cypress.env('AccessTokenUrl') +
'?grant_type=' + Cypress.env('GrantType') +
'&client_id=' + Cypress.env('ClientId') +
'&client_secret=' + Cypress.env('ClientSecret'))
})
This is what I call in a test:
cy.login().then(response => {
expect(response.status).to.eq(200)
expect(response.body).to.have.property('access_token')
expect(response.body).to.have.property('token_type', 'Bearer')
const {access_token, expires_in, id_token} = response.body
cy.setCookie('access_token', access_token)
})
cy.visit('my-url')
The validations pass. The login response contains a valid token. However, the ct.visit call fails with infinite recursion, as the parameters like &iframe-request-id=[some uuid] become added over and over to a login.microsoftonline.com URL, until eventually returning HTTP Error 414. The request URL is too long.
Here's what the URL looks like, with some information redacted and with some formatting for clarity:
https://login.microsoftonline.com/
[tenant-id]/oauth2/v2.0/authorize
?response_type=code
&client_id=[client-id]
&redirect_uri=[my-url]
&scope=openid+profile+email+https%3A%2F%2Fgraph.microsoft.com%2Fuser.read
&iframe-request-id=1a9fdcbd-6b9e-46c8-93e3-ce0edf62b600
&iframe-request-id=b5b5cf2b-e0a6-4d92-9e55-cf32208ab900
&iframe-request-id=8471e17f-1d36-48f7-8419-f54e14b3b100
&iframe-request-id=56113dad-6029-4a37-9758-5828f93f0300
&iframe-request-id=51c06224-98f1-4b83-a8f2-84f8dfe9aa00
&iframe-request-id=09775645-505c-42e0-ac56-1335b5a7ba00
&iframe-request-id=5c98158b-b202-41fe-9d65-8fbfe4e46500
&[and-so-on]
I have found various suggestions on the web about using Puppeteer as a task for Azure AD SSO, but none of them works for my purposes. First, they try to resolve the problem of actually obtaining the token, which I have already solved. Second, they rely on the login URL presenting an HTML form, which is not the case with login.microsoftonline.com.
What do you suggest?
UPDATE: Trying a different solution, I receive an interesting error. The loginMS command:
import * as MSAL from '#azure/msal-browser'
Cypress.Commands.add('loginMS', () => {
cy.request({
method: 'POST',
url: `https://login.microsoftonline.com/${Cypress.env('TenantId')}/oauth2/token`,
form: true,
body: {
scope: Cypress.env('LoginScope'),
client_id: Cypress.env('ClientId'),
client_secret: Cypress.env('ClientSecret'),
redirect_uri: Cypress.env('LoginRedirect'),
grant_type: Cypress.env('GrantType'),
username: Cypress.env('Username'),
password: Cypress.env('Password'),
response_type: 'code'
}
}).then(response => {
console.log(response)
window.localStorage.setItem(`msal.idtoken`, response.body.access_token);
window.localStorage.setItem(`msal.client.info`, MSAL.clientInfo);
})
})
The error is:
Failed to find a valid digest in the 'integrity' attribute for resource
'https://aadcdn.msauth.net/shared/1.0/content/js/OldConvergedLogin_PCore_Up8WrFIk8-TG_eqBz8MSlw2.js'
with computed SHA-256 integrity 'NxfOkHjbTYDy/EOknsK0PMOfym7iLRGY+yBShyznzx4='.
The resource has been blocked.
It realy depends how the application under test handles requests. But I guess you use the adal libary.
With the help of https://mechanicalrock.github.io/2020/05/05/azure-ad-authentication-cypress.html it worked for me in a vuejs application using adal v1.
The important part is
localStorage.setItem("adal.token.keys", `${Cypress.config("clientId")}|`);
localStorage.setItem(`adal.access.token.key${Cypress.config("clientId")}`, ADALToken);
localStorage.setItem(`adal.expiration.key${Cypress.config("clientId")}`, expiresOn);
localStorage.setItem("adal.idtoken", ADALToken);
I actually did not request the token from azure but just copied in what I saw F12 tools as my token when using the application under test.
I managed to solve the Azure AD login by creating the following Cypress custom command for my Angular application:
Cypress.Commands.add('login', () => {
return cy
.request({
method: 'POST',
url: `https://login.microsoftonline.com/${tenantId}/oauth2/token`,
form: true,
body: {
grant_type: 'password',
tenant: 'tenantId',
client_id: 'clientId',
client_secret: 'clientSecret',
username: 'username',
password: 'password',
resource: 'clientId',
},
})
.then((response) => {
sessionStorage.setItem('access_token', response.body.access_token);
});
});

MS graph API: 400 AuthenticationError with "/me/onlineMeetings" request

I am trying to create an online meeting and recover its URL like explained here in the docs, but when the request is run I get this error:
{
"statusCode": 400,
"code": "AuthenticationError",
"message": "Error authenticating with resource",
"requestId": "652ea3be-6a97-47e8-bfc6-3d7d1d51d425",
"date": "2020-09-01T12:53:41.000Z",
"body": "{
"code":"AuthenticationError",
"message":"Error authenticating with resource",
"innerError":{
"date":"2020-09-01T13:53:41",
"request-id":"652ea3be-6a97-47e8-bfc6-3d7d1d51d425"
}
}"
}
I tried also the get started projet for JS and it's working fine so I can't spot the problem.
here is what I used:
const msalConfig = {
auth: {
clientId: 'my_app_id',
redirectUri: 'http://localhost:8080'
},
cache: {
cacheLocation: "sessionStorage",
storeAuthStateInCookie: false,
forceRefresh: false
}
};
const loginRequest = { scopes: [
'openid',
'profile',
'user.read',
'calendars.read',
'User.Read.All',
'User.Export.All'
]
}
const options = new MicrosoftGraph.MSALAuthenticationProviderOptions([
'user.read',
'calendars.read',
'OnlineMeetings.ReadWrite'
]);
const onlineMeeting = {
startDateTime:"2020-09-01T16:00:34.2444915-07:00",
endDateTime:"2020-09-01T16:30:34.2464912-07:00",
subject:"test meeting"
};
const authProvider = new MicrosoftGraph.ImplicitMSALAuthenticationProvider(msalClient, options);
// Initialize the Graph client
const graphClient = MicrosoftGraph.Client.initWithMiddleware({authProvider});
// then I call this inside an async function
let events = await graphClient.api('/users/my_UserPrincipalName/onlineMeetings').post(onlineMeeting);
//let events = await graphClient.api('/me/onlineMeetings').post(onlineMeeting);
// I tried with both calls and none of them worked
and here are the permissions on azure active directory:
So any ideas on how to solve this ?
thanks
You didn't provide a correct access token.
Since Create onlineMeeting only supports Delegated (work or school account) permission type, you need to get the access token with Auth code flow or Implicit flow.
The started project for JS is using Implicit flow. So you can use Implicit flow to get the access token.
Here is the example in Postman:
The Auth URL above is https://login.microsoftonline.com/{your tenant}/oauth2/v2.0/authorize.
I figured out how to make it work in my code:
let's call my user, which I used all this time, user "A", all I did is that I simply created another user "B" in Azure Active Directory and then logging in with this new user "B" in the login screen instead of the admin user "A" that I used before..... and now it's working.
But this does not explain the issue, so if anyone can explain the difference or why it didn't work with the first account, that would be very helpful.

AADSTS50058: A silent sign-in request was sent but no user is signed in

I am using hello.js to sign in Microsoft Graph.
First I initialized by
hello.init({
msft: {
id: myAppId,
oauth: {
version: 2,
auth: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize'
},
scope_delim: ' ',
form: false
},
},
{ redirect_uri: window.location.href }
);
Then I signed in successfully in my app
hello('msft').login({ scope: 'User.Read' })
This is what hello.js saved in localStorage after signing in.
{
"msft": {
"access_token":"aLongToken",
"token_type":"Bearer",
"expires_in":3599,
"scope":"basic,User.Read",
"state":"",
"session_state":"f034f785-f8d0-4cec-aab4-88559c9d93dd",
"client_id":"a91e6907-2b6e-4793-848d-633e960e809d",
"network":"msft",
"display":"popup",
"redirect_uri":"http://localhost:3006/login",
"expires":1501800737.361
}
}
However, when I try to refresh the access_token
hello('msft').login({
display: 'none',
response_type: 'id_token token',
response_mode: 'fragment',
nonce: 'my-app',
prompt: 'none',
scope: 'User.Read',
login_hint: 'Rose.Bukater#company.com',
domain_hint: 'organizations'
})
I got the error
AADSTS50058: A silent sign-in request was sent but no user is signed
in. The cookies used to represent the user's session were not sent in
the request to Azure AD. This can happen if the user is using Internet
Explorer or Edge, and the web app sending the silent sign-in request
is in different IE security zone than the Azure AD endpoint
(login.microsoftonline.com).
I am using Chrome.
Found this issue on GitHub. But still didn't figure out how to refresh correctly.
UPDATE:
After disable Allow Implicit Flow at https://apps.dev.microsoft.com, now I even failed to log in. So this is not the correct solution. hello.js saved this error in the localStorage:
{
"msft": {
"error": {
"code":"unsupported_response_type",
"message":"AADSTS70005: response_type 'token' is not enabled for the application\r\nTrace ID: 1dc20dd0-cab3-41b5-9849-2a7e35d60700\r\nCorrelation ID: caacce8f-6763-405d-a840-70c24d5306d4\r\nTimestamp: 2017-08-04 21:56:42Z"
},
"error_description":"AADSTS70005: response_type 'token' is not enabled for the application\r\nTrace ID: 1dc20dd0-cab3-41b5-9849-2a7e35d60700\r\nCorrelation ID: caacce8f-6763-405d-a840-70c24d5306d4\r\nTimestamp: 2017-08-04 21:56:42Z",
"state":"",
"client_id":"a91e6907-2b6e-4793-848d-633e960e809d",
"network":"msft",
"display":"popup",
"redirect_uri":"http://localhost:3006/login",
"scope":"basic,User.Read"
}
}
It happens when the cookie of the user currently connected for login.microsoftonline.com has expired. The way we handle it is we redirect the user to sign in page with current page as redirecturi parameter.
I found the issue. My code in the question is totally correct. The reason causing this issue is because in our company each person has two emails:
one is full name email Rose.Bukater#company.com
one is alias email rosebuk#company.com, which is the property userPrincipalName
For login_hint below, it has to be the alias email.
hello('msft').login({
display: 'none',
response_type: 'id_token token',
response_mode: 'fragment',
nonce: 'my-app',
prompt: 'none',
scope: 'User.Read',
login_hint: 'Rose.Bukater#company.com', // <- has to be rosebuk#company.com
domain_hint: 'organizations'
})

Categories

Resources