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'],
},
Related
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
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'
}
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);
});
});
Authenticated user can still access the /login page. If I follow a link to /login page, I am redirected to a different page, which works fine. But if I enter /login into URL manually, I am still being taken to /login page, even though I am already logged in. What I am trying to achieve is, when the user is logged in, they should be redirected to /retailer/account page before any components are shown on the page (if they enter /login page manually).
I was trying using beforeMount() function, beforeCreate() function, following the documentation for auth: 'guest' middleware, however it does not seem to have any effect and loggedIn always returns false before the page is fully rendered.
My setup:
nuxt.config.js :
export default {
mode: 'universal',
target: 'static',
auth: {
cookie: {
prefix: 'auth_'
},
// Options
redirect: {
login: '/login',
logout: '/login',
callback: false,
home: '/retailer/account'
},
strategies: {
local: {
endpoints: {
login: {
url: '/auth/login',
method: 'post',
propertyName: 'access_token',
credentials: true
},
logout: {
url: '/auth/logout',
method: 'post'
},
user: {
url: '/auth/me',
method: 'post',
propertyName: '',
credentials: false
}
},
token: {
required: true,
type: 'Bearer',
global: true
},
autoFetchUser: true
}
},
preserveState: true,
watchLoggedIn: true
},
router: {
middleware: [
'auth'
]
}
}
layouts/default.vue :
export default {
auth: false
}
pages/retailer/account.vue :
export default {
middleware: 'auth'
}
pages/login.vue :
export default {
middleware: 'authenticated',
auth: 'guest'
}
Tried all kinds of middleware examples I could find and made some simple redirection code, but, as mentioned before, app.$auth.loggedIn always returns false from server-side, so I never get redirected.
middleware/authenticated.js
export default function ({ app, redirect }) {
if (app.$auth.loggedIn) {
return redirect(app.localePath({ name: 'index' }))
}
}
Later, when Vue hydrates the app, loggedIn is, of course, true.
Any ideas what I am doing wrong? Any guidance would be a huge help!
Nuxt auth module has a middleware auth can redirect unauthenticated user to login page, and redirect authenticated to home.
I see than the auth middleware is configured correctly in you nuxt.config.js.
The problem is that auth middleware only works on server side for SSR. but you configured to generate static website.
You can remove line target: 'static', from nuxt.config.js.
Or fllow this issue to find some help for static site auth:
https://github.com/nuxt/nuxt.js/issues/3023
Recently i started to learn how to use Nuxtjs and while learning how to use its Auth module i came across a problem.
I'm able to log in and I want to check the scope of the accounts, i tried doing it using the "scopeKey" property of "Auth". From the back end i get the "scope" from the databse and it can either be "user" or "admin".
I have tried to set the scope with
scopeKey: 'scope'
But I get that scope is "undefined"/"null" when checking with
this.$auth.hasScope('admin') / this.$auth.hasScope('user')
or "this.$auth.hasScope(admin)" return an empty value when setting "scopeKey" to
scopeKey: 'data.scope'
or
scopeKey: 'user.scope'
Here is my auth strategy:
auth: {
strategies: {
local: {
scopeKey: 'scope',
endpoints: {
login: {
url: 'api/auth/login',
method: 'post',
propertyName: 'token',
},
logout: {
url: 'api/auth/logout',
method: 'get'
},
user: {
url: 'api/me',
method: 'get',
propertyName: data
}
}
}
},
redirect: {
login: '/auth/login',
logout: '/',
callback: '/auth/login',
home: '/dash/'
}
}
and here it is an example of the json that the auth module reads when i log in:
"data": {
"id": 2,
"name": "test1",
"email": "test#test.com",
"email_verified_at": null,
"scope": "admin",
"created_at": "2019-08-01 13:11:49",
"updated_at": "2019-08-01 13:11:49"
},
I can access the scope value on the front end page with
$auth.user.scope
or with
$auth.$state.user.scope
But how can I give the "scope" to the "scopeKey" property in the nuxt.config.js file while setting the "auth" properties/strategy?
edit:
I have tried moving it inside the auth object or deleting the property and I still get false on $auth.hasScope('admin') and $auth.hasScope('user') which means scopeKey is still undefined and i'm not sure why.
The scopeKey is 'scope' by default. You don't need to set it again.
For me it worked by doing server side change.
When I put string value in scope, it does not work
$data['scope'] = "admin";
But when I changed it to array, $auth.hasScope('admin') works;
$data['scope'] = array("admin", "test");
Hope it helps.
scopeKey: 'scope' should not be placed inside strategies object.
Put it directly in the auth object.
Take a look at default config.
P.S. You can even delete this property from your auth config object, because 'scope' is default value for scopeKey.
Somehow hasScope() is not working for me too, so I directly checked the user object, I also has token in my response.
I am having a type variable in my response that tells me if the user is admin or someone else.
add this into your middleware
export default function ({ $auth, redirect }) {
if (!$auth.loggedIn) {
return redirect('/')
}
if ($auth.user.type != 'Super Admin') { // Super Admin or whatever the user you want to check
return redirect('/')
}
}