atinux / nuxt-auth-utils Goto Github PK
View Code? Open in Web Editor NEWMinimal Auth module for Nuxt 3.
License: MIT License
Minimal Auth module for Nuxt 3.
License: MIT License
I tried to use nuxt-auth-utils with Safari on macOS 14.4.1, but I've never been able to get it to work.
The cookie nuxt-session
is never received on Safari, but it's working on Arc.
To reproduce:
http://localhost:3000/auth/auth0
for exampleauth0.get.ts
is executed without error. If I log user
from the onSuccess
event handler, I get the information like expected.nuxt-session
cookieBut I can get https://todos.nuxt.dev/ works on Safari.
Is there anything I'm missing?
Hello, I was giving discord a try:
{
label: 'Discord',
icon: 'i-simple-icons-discord',
color: 'indigo',
click: () => {
loading.value = true
window.location.href = '/api/auth/discord'
}
}
Inside of ~/server/api/auth/discord.get.ts
I have
export default oauth.discordEventHandler({
config: {
emailRequired: true,
profileRequired: true,
},
async onSuccess(event, { user, tokens }) {
console.log('Discord OAuth success:', user)
await setUserSession(event, {
user
})
return sendRedirect(event, '/dashboard')
},
// Optional, will return a json error and 401 status code by default
onError(event, error) {
console.error('Discord OAuth error:', event, error)
return sendRedirect(event, '/')
},
})
I am able to load the discord authorization screen, but when it performs the callback, I get an error:
Discord OAuth error: H3Event {
__is_event__: true,
node:
{ req:
IncomingMessage {
_events: [Object],
_readableState: [ReadableState],
_maxListeners: undefined,
socket: [Socket],
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
rawHeaders: [Array],
rawTrailers: [],
joinDuplicateHeaders: null,
aborted: false,
upgrade: false,
url: '/api/auth/discord?code=04yK3Z0BVKV2B83O3UVZooaTZa2G0f',
method: 'GET',
statusCode: null,
statusMessage: null,
client: [Socket],
_consuming: false,
_dumped: false,
originalUrl: '/api/auth/discord?code=04yK3Z0BVKV2B83O3UVZooaTZa2G0f',
[Symbol(shapeMode)]: true,
[Symbol(kCapture)]: false,
[Symbol(kHeaders)]: [Object],
[Symbol(kHeadersCount)]: 36,
[Symbol(kTrailers)]: null,
[Symbol(kTrailersCount)]: 0 },
res:
ServerResponse {
_events: [Object: null prototype],
_eventsCount: 1,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: false,
chunkedEncoding: false,
shouldKeepAlive: false,
maxRequestsOnConnectionReached: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: true,
sendDate: true,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
strictContentLength: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: false,
_headerSent: false,
_closed: false,
_header: null,
_keepAliveTimeout: 5000,
_onPendingData: [Function: bound updateOutgoingData],
req: [IncomingMessage],
_sent100: false,
_expect_continue: false,
_maxRequestsPerSocket: 0,
[Symbol(shapeMode)]: false,
[Symbol(kCapture)]: false,
[Symbol(kBytesWritten)]: 0,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kChunkedBuffer)]: [],
[Symbol(kChunkedLength)]: 0,
[Symbol(kSocket)]: [Socket],
[Symbol(kOutHeaders)]: null,
[Symbol(errored)]: null,
[Symbol(kHighWaterMark)]: 16384,
[Symbol(kRejectNonStandardBodyWrites)]: false,
[Symbol(kUniqueHeaders)]: null } },
web: undefined,
context:
{ _nitro: { routeRules: {} },
nitro: { errors: [], runtimeConfig: [Object] },
matchedRoute: { path: '/api/auth/discord', handlers: [Object] },
params: {} },
_method: 'GET',
_path: '/api/auth/discord?code=04yK3Z0BVKV2B83O3UVZooaTZa2G0f',
_headers: undefined,
_requestBody: undefined,
_handled: false,
fetch: [Function (anonymous)],
'$fetch': [Function (anonymous)],
waitUntil: [Function (anonymous)],
captureError: [Function (anonymous)] } Discord login failed: Unknown error
Any ideas?
Hello Nuxters/Vuetists 🔥💚,
I was playing for hours to solve my issue.
If I use useFetch('/api/auth/github', {redirect: 'follow'})
, I get error:
(redirected from 'http://localhost:3000/api/auth/github') from origin 'http://localhost:3000' has been blocked by CORS policy: No 'vi-tr.js:1
GET https://github.com/login/oauth/authorize?client_id=*****&redirect_uri=http://localhost:3000/api/auth/github&scope=user:email net::ERR_FAILED 302 (Found)
login:1 Access to fetch at 'https://github.com/login/oauth/authorize?client_id=*****&redirect_uri=http://localhost:3000/api/auth/github&scope=user:email' (redirected from 'http://localhost:3000/api/auth/github') from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled..
Then if I use navigateTo('/api/auth/github')
:
And when I refresh the page with url http://localhost:3000/api/auth/github
, then it redirects to Github login page.
So my only options are:
navigateTo('/api/auth/github', {open: {target: '_self'}})
:window.location.href = '/api/auth/github'
Wanna read your thoughts. 👋
How can we extend this to support username+password auth?
I understand it may involve some kind of db configuration to store user data
Im try to use the youtube service and i try to add the some more scopes like youtube.readonly
, etc... When I did this I have an invalid_scope
error. Do you have any solution or something to advice?
Some requested scopes were invalid. {valid=[https://www.googleapis.com/auth/userinfo.email, https://www.googleapis.com/auth/userinfo.profile], invalid=[youtube.readonly]}
This provider is becoming challenger.
https://casdoor.org
Currently, users will set up their endpoints by accessing one of the oauth.*
.
export const oauth = {
githubEventHandler,
spotifyEventHandler,
googleEventHandler,
twitchEventHandler,
auth0EventHandler,
microsoftEventHandler,
discordEventHandler,
battledotnetEventHandler,
keycloakEventHandler,
linkedinEventHandler,
cognitoEventHandler
};
This works and the auto-completion is nice but I think it can be improved:
oauth
seems likely for collisions in global import namespaceExporting each provider as its own function should fix tree shaking and namespace problems, i.e authGoogleEventHandler
where autocompletion should be good.
I have a global middleware that looks something like this.
const { loggedIn, user, clear } = useUserSession()
// If user is logged in but permissions are not loaded, then force the user to re-login
if (loggedIn?.value && !user?.value?.permissions) {
await clear()
return navigateTo(`/login`)
}
Calling the clear method during SSR results in the following error.
Feb 26 14:27:09 xxx.com node[152599]: Error clearing user session Error: [nuxt] instance unavailable
Feb 26 14:27:09 xxx.com node[152599]: at useNuxtApp (file:///xxx/.output/server/chunks/app/server.mjs:1503:13)
Feb 26 14:27:09 xxx.com node[152599]: at useState (file:///xxx/.output/server/chunks/app/server.mjs:2104:35)
Feb 26 14:27:09 xxx.com node[152599]: at useSessionState (file:///xxx/.output/server/chunks/app/server.mjs:2140:31)
Feb 26 14:27:09 xxx.com node[152599]: at clear (file:///xxx/.output/server/chunks/app/server.mjs:2160:3)
Feb 26 14:27:09 xxx.com node[152599]: at async file:///xxx/.output/server/chunks/app/server.mjs:2191:59
Feb 26 14:27:09 xxx.com node[152599]: at async Object.callAsync (file:///xxx/.output/server/chunks/app/server.mjs:90:16)
Feb 26 14:27:09 xxx.com node[152599]: at async file:///xxx/.output/server/chunks/app/server.mjs:2336:26
Congratulations on the release of this awesome module. I have been waiting for it since I tried h3's sessions.
Question: How to make this work in a SPA (ssr: false
in nuxt.config.ts
)? Thank you.
When authorizing with google, these params are hardcoded
response_type: "code",
client_id: config.clientId,
redirect_uri: redirectUrl,
scope: config.scope.join(" ")
However, several other options can be provided here. See https://developers.google.com/identity/protocols/oauth2/web-server#httprest_3
What is currently blocking me is access_type
that allows you to specify offline
, which is the only way to get a refresh_token
, which is the only way to keep the user logged in for more than 1 hour (when they need to access Google APIs).
Maybe an option like useRuntimeConfig(event).oauth.google.authorizationParams
config: {
redirectUrl: '/auth/google',
scope: [
'https://www.googleapis.com/auth/userinfo.email',
],
authorizationParams: {
prompt: 'consent',
access_type: 'offline',
},
},
I'm curious about the possibility of writing an OAuth provider in userland code. I'm currently interacting with one that doesn't seem to justify inclusion in this library due to its limited usage within a small region. Can anyone provide insights on how this can be achieved? Thank you!
Would be great if the config would have a key like authorization_params
where we can add any optional parameter the provider expects.
In my example I would like to add ?access_type=offline
to the authorizationURL
for Google (So that it returns also the refresh token). Having one general authorization_params
object would avoid having to add different parameters for each provider manually.
Ideally I would have something like:
export default oauth.googleEventHandler({
config:{
authorization_params: { // Something like this would work perfectly
access_type:"offline"
},
scope: [
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/tasks.readonly' // This is great!
]
},
async onSuccess(event, { user, tokens }) {
console.log(tokens.refresh_token) // Save this in DB
await setUserSession(event, { user, loggedInAt: Date.now() })
return sendRedirect(event, '/')
}
})
From my understanding this is currently impossible, right?
I'm using this module for Auth widgets, but I'm currently having a problem that the data is not updated in time. Here are a few examples:
"devDependencies": {
"@hypernym/nuxt-anime": "^2.1.1",
"@nuxt/devtools": "latest",
"@nuxtjs/color-mode": "^3.3.2",
"@nuxtjs/tailwindcss": "^6.10.1",
"nuxt": "^3.9.0",
"nuxt-auth-utils": "^0.0.13",
"vue": "^3.3.12",
"vue-router": "^4.2.5"
}
In node .output/server/index.mjs
, you can never log in successfully
It seems like the types generated in .nuxt/types/nitro-imports.d.ts
imports from the wrong files.
The utilities (setUserSession, getUserSession etc.) are being imported from the .mjs
files as seen in the screenshot below.
This causes everything to be typed as any
, as in this githubEventHandler example:
I guess this might have something to do with the way the utils are imported here?
The cookie created with setUserSession
is configured to expire with the browser session.
How do I persist the cookie for a longer time?
I was sad to see that the closest thing currently available is the Keycloak provider however that mandates usage of a realm. Many OIDC providers providers, especially FOSS ones which you can self-host provide a standardized well-known/openid-configuration
endpoint where all further endpoints and supported values are exposed. It would be great to support this and would eliminate the need for many specialized providers. Ideally, there is a general manualOIDC provider where one can manually set the authorization/token/userinfo/revokation etc endpoint and one wellKnownOIDC provider which simply takes a single URL, fetches the values and delegates the rest to the manualOIDC provider.
(context: question by @harlan-zw in nuxt discord regarding reliablilty of sessions and weather he should use storage to keep private session data)
H3 sessions are encrypted and only readable by server-side. This can guarantee two things:
Auth-utils, exposes an edpoint (session.get
) that server-side decrypts the session for user. It takes away the second benefit of session encoding which can guarantee data remains secret and private.
While there must be good benefits of this, it is something IMO insecure to do by default and developers might wrongly put sensitive data based on encryption guarantee that will be exposed again.
I would highly recommend (as a breaking change) to only expose data.public
part of the session.
My question would be similar to #10 , support authentication with ASP.NET Core Identity
The naming of the function setUserSession
suggest you can set data to what you provide as an argument. In fact new data is always merged with old session data.
/**
* Set a user session
* @param event
* @param data User session data, please only store public information since it can be decoded with API calls
*/
export async function setUserSession (event: H3Event, data: UserSession) {
const session = await _useSession(event)
await session.update(defu(data, session.data))
return session.data
}
defu(data, session.data)
merges data deeply. If you want to actually delete data, you cannot do it. In addition to this session.update
will perform an object.assign call (shallow merge).
I think we should have different functions for setting and updating(merging) session data.
Hey,
I'm trying to store the auth token (jwt) in the session (to pass it in the header for fetch)
This works
await setUserSession(event, {
user,
loggedInAt: Date.now(),
thing: 'isathing',
});
This doesn't work
await setUserSession(event, {
user,
loggedInAt: Date.now(),
thing: 'nwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2osnwnn10eyu9mfeqnf1j1ry4gdagcyv2os',
});
There's no explicit error but my guess is that it's related to the length
Either there's a bug or i'm not supposed to store the auth token here?
Thanks for the help (and thanks for building this module!)
The latest version of nuxt#3.8.2
or the latest [email protected]
breaks the module, with this error:
[Vue Router warn]: No match found for location with path "/api/_auth/session"
On older projects it works fine, when I run npx nuxi upgrade
it breaks, I'm not sure what causes this, but my best bet is the latest version of h3/nitro
Hi
First, thanks for the great work
Currently, I'm working on a POC to migrate our Vue 3 SSR app to Nuxt 3 and I'm trying a few libs to replace the express-openid-connect that we are using to manage the authentication.
The question is that on each request to our APIs I need to send the token and also check if the token is still valid.
From what I see from the example, this is only done during the authentication process.
Is it possible to access/expose the tokens that are returned at onSuccess from
nuxt-auth-utils/src/runtime/server/lib/oauth/auth0.ts
Lines 87 to 126 in 10398a6
Thanks
PS: Maybe it would be great to have a "Discussions" page for this kind of questions
I saw the "offline_access"
scope being used for the OAuth0 provider but no reference to refresh tokens in the codebase. Are refresh tokens implemented/utilized? Or is the session from the OAuth2 provider only used once and afterwards everything is delegated to h3?
Thanks a lot Atinux for the great work!
This module is very clean and easy to understand!
My question is what are the reasons for which it is only server-side, by looking at the endpoints it seems that code could easily be in pages or composables, so, why the limitation?
I call my endpoint /api/countries
which is doing a request to an external API which needs info from the session. That works fine.
But when I call my api route on the initial loading of the app, for example in app.js
in a callOnce
, it seems that the session is not available yet.
// This seems not to work
await callOnce(async () => {
const result = await $fetch('/api/countries')
})
Am I doing something wrong or is this correct behavior?
The idea was to fetch data and filling the store on app load. How can this be achieved with using a token from the authentication?
Hello! awesome module! Is there any example how to implement my own auth with nitro and sqlite (JWT/session)
I'm looking to create E2E test scenarios that include the login of an application. Currently, I'm using Playwright, but it cannot mock server-side calls, unfortunately.
I'm working on a workaround by implementing an oauth2 mock server that accepts the contracts as Google, but, there is any other suggestion to make it work, or do you see any improvement that can be implemented to ease that kind of usage? Seems that approach is kind hacky.
Sometimes we won't know ahead of time the exact config to use for an oauth request, it would be nice if we could provide config
as a function that takes the event
property.
It's related to this issue: #48.
Bit edge case but I think it's a fairly simple change that provides much more flexibility.
config(event) {
// do logic
},
While reading the codebase (to potentially implement #89, most likely based on the existing Keycloak provider) I noticed that some places use ofetch and others use $fetch. Is there a specific reason for this? Otherwise it might be wise to unify them and use one of the two consistently throughout the codebase.
Related to https://pilcrow.vercel.app/blog/local-storage-cookies
Check the origin for requireUserSession as well as SameSite=Lax
The playground project tries to augment UserSession
:
nuxt-auth-utils/playground/auth.d.ts
Line 3 in e344c98
But user
is already declared here:
🤔I don't think it is possible to augment an already declared field of an interface.
It has been bugging me for a while that the types of this module do not work at all.
Lets say this is your auth.d.ts
:
declare module '#auth-utils' {
interface UserSession {
user: {
id: number
name: string
email: string
}
loggedInAt: number
}
}
On the client-side, all properties of the useUserSession
composable are of type any
(only in the playground it works)
On the server-side, UserSession
is defined except for the user
object.
Its type will always be {} | undefined
regardless of what is specified in the auth.d.ts
(see issue #31 )
And when checking for a session using requireUserSession
the returned user
object is still possibly undefined
even though we just checked and required it to not be.
Instead of looking for a specific property in the session data (user
in this case), we check if there are any properties at all.
export async function requireUserSession(event: H3Event) {
const userSession = await getUserSession(event)
if (Object.keys(userSession).length === 0) {
throw createError({
statusCode: 401,
message: 'Unauthorized'
})
}
return userSession
}
This way you can define anything in the UserSession
interface and the type works (this uses the playground's auth.d.ts
)
On the client-side I made the following changes:
loggedIn
is now computed based on wether there exist properties on the session objectuser
getter as this property is no longer defined but now the typing is at least correct.const useSessionState = () => useState<UserSession>('nuxt-session', () => ({}))
export const useUserSession = () => {
const sessionState = useSessionState()
return {
loggedIn: computed(() => Object.keys(sessionState.value).length > 0),
session: sessionState,
fetch,
clear
}
}
I would like to hear feedback from you guys
The API paths for getting and deleting sessions are hard-coded:
[get] api/_auth/session
[delete] api/_auth/session
The configurable APIs allow users to specify how they want to get and delete sessions.
Currently, in the module definition, information about the session and cookie, such as the name, password, and type of cookie, is read from the runtimeConfig.session
. In the same way, the main path of the session API (for the get and delete methods) should be configurable, for example from module options.
Was curious if Laravel Sanctum is on the roadmap to becoming a provider. I currently use Nuxt Auth for my Nuxt2 Applications and really enjoyed how simple that was to get Laravel Sanctum working with. Have recently started several new Nuxt3 projects with a Laravel backend.
Any info would be great, Thanks!
We are trying to use this with Azure Static Web App, but the redirect url is wrong.
From what i can see it gets the redirectUrl from getRequestUrl(event).href
, this does not work with SWA since the hostname refers to an internal url for the function app.
You can see an example output here:
https://ambitious-dune-01c6e3503-preview.westeurope.4.azurestaticapps.net/api/info
Not sure what the best approach for this would be. X-Forward-For header contains only IP Adresse (i assume this is internal infrastructure at Microsoft). SWA does have a special header called 'x-ms-original-url' that contains the original url, it seems very specific for SWA and would only apply to apps running in Azure SWA.
One possible solution would be to allow a configured redirectUrl (but its not the best solution IMO, in case we have multiple domains pointing to the same instance), another would be to create a custom handler for apps running in SWA.
Any suggestions on the best way to handle this?
when running npm run dev:build
I get the following error:
ERROR You must provide the NUXT_UI_PRO_LICENSE environment variable. 3:12:14 PM
Purchase Nuxt UI Pro at https://ui.nuxt.com/pro/purchase to build your app for production.
I have set:
NODE_ENV="development"
but it does not help
I don't want to deploy anything to production so I should not need the license, correct? Can I set some ENV variable to make Nuxt UI Pro recognize this?
While implementing session management with the nuxt-auth-utils library, I encountered a very hard to debug issue where the session fails to set/update with a slightly large amount of data.
One of the user entered a large amount of data to my users table field called "bio", the next time they logged in this data was getting included in the session, I have now removed this added a limit to this field. This kept breaking auth flow because the session was never getting set, and always returned a 401 when this specific user tried logging in, this can be handled in the setUserSession
method
nuxt-auth-utils Version: ^0.0.22
Nuxt Version: 3.11.2
Browser/Node.js Version: Chrome 123.0.6312.107
Platform: Macos Sonoma
I've already seen #9 but wanted to confirm because the requirements highlight:
You cannot use this module with
nuxt generate
.
Does this mean we can use this module in a Nuxt project where the landing page runs with SSR but the logged in pages are an SPA? Or does it mean the whole auth flow must happen within an SSR context?
I've tried to migrate to this module in my SaaS starter template but it seems it cannot find the user session if I redirect to an SPA page after log in, although if I refresh the page, it returns the session fine.
I need a simple example on how to use Laravel Passport 🙏
@jfrelik ; thanks for the Microsoft provider. Works great to authenticate against MS Graph.
How do you use it to authenticate against your own API Application? I am stuck, as when I add the access_as_user scope in the microsoftEventHandler config section.
scope: ['api://{my_application}/access_as_user','openid','offline_access', 'profile','User.Read',], // USE AN ARRAY FOR YOUR SCOPES
this will allow me to call my own API, as only access_as_user will be included in scp of the access_token.
scope: ['openid','offline_access', 'profile','User.Read','api://{my_application}/access_as_user',], // USE AN ARRAY FOR YOUR SCOPES
this will log-me in correctly and allow me to call the grap, as all but the access_as_user scp is in the access token.
Do I need two access tokens? One to access the Graph, and one for my API?
Hey @Atinux, I am using this library and had a few questions if I can use this with a email/password setup.
I am using it as below
import { Argon2id } from "oslo/password";
import { useValidatedBody, z } from "h3-zod";
import { eq } from "drizzle-orm";
const validationSchema = z.object({
email: z.string().email(),
password: z.string(),
});
export default defineEventHandler(async (event) => {
const { email, password } = await useValidatedBody(event, validationSchema);
const db = useDB();
const argon2id = new Argon2id();
// Attempt to find the user by email
const users = await db
.select()
.from(tables.users)
.where(eq(tables.users.email, email));
if (users.length === 0) {
// No user found with the provided email
throw createError({
statusCode: 401,
statusMessage: "Invalid email or password",
});
}
const user = users[0];
// Verify the password
const validPassword = await argon2id.verify(user.password, password);
if (!validPassword) {
// Password does not match
throw createError({
statusCode: 401,
statusMessage: "Invalid email or password",
});
}
delete user.password;
// Password matches, set the user session
await setUserSession(event, { user });
return user;
});
When I login in the client form and use the navigateTo composable, the session is set but in client it's still empty so my middle always redirects it back to the index page
<template>
<div class="flex min-h-screen flex-1">
<div
class="flex flex-1 flex-col justify-center px-4 py-12 sm:px-6 lg:flex-none lg:px-20 xl:px-24"
>
<div class="mx-auto w-full max-w-sm lg:w-96">
<div>
<img class="h-10 w-auto" src="/logo.png" alt="Fullstack" />
<h2
class="mt-8 text-2xl font-bold leading-9 tracking-tight text-gray-900 font-display"
>
Sign in to your account
</h2>
<p class="mt-2 text-sm leading-6 text-gray-500">
Not a member?
{{ " " }}
<a
href="#"
class="font-semibold text-primary-600 hover:text-primary-500"
>Start a 14 day free trial</a
>
</p>
</div>
<div class="mt-10">
<div>
<UForm
:schema="schema"
:state="state"
class="space-y-6"
@submit="onSubmit"
>
<UFormGroup label="Email" name="email" size="xl">
<UInput v-model="state.email" />
</UFormGroup>
<div class="relative">
<NuxtLink
href="#"
class="text-gray-500 text-xs hover:text-gray-400 leading-6 absolute right-0 -top-1"
>Forgot password?</NuxtLink
>
<UFormGroup label="Password" name="password" size="xl">
<UInput v-model="state.password" type="password" />
</UFormGroup>
</div>
<UButton
type="submit"
size="xl"
label="Submit"
block
:loading="loading"
:disabled="loading"
color="black"
/>
</UForm>
</div>
<div class="mt-10">
<UDivider label="Or continue with" />
<div class="mt-6 grid grid-cols-2 gap-4">
<UButton
to="/api/auth/google"
external
size="lg"
color="white"
block
>
<Icon class="h-5 w-5" name="i-logos-google-icon" />
<span>Google</span>
</UButton>
<UButton
to="/api/auth/github"
external
label="Github"
icon="i-simple-icons-github"
size="lg"
color="white"
block
/>
</div>
</div>
</div>
</div>
</div>
<div class="relative hidden w-0 flex-1 lg:block">
<img
class="absolute inset-0 h-full w-full object-cover"
src="https://images.unsplash.com/photo-1496917756835-20cb06e75b4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1908&q=80"
alt=""
/>
</div>
</div>
</template>
<script setup>
definePageMeta({ layout: "dashboard", colorMode: "light" });
import { z } from "zod";
const { loggedIn } = useUserSession();
const loading = ref(false);
const toast = useToast();
const schema = z.object({
email: z.string().email(),
password: z
.string()
.min(8, "Password must be at least 8 characters long")
.regex(/[A-Z]/, "Password must include at least one uppercase letter")
.regex(/[0-9]/, "Password must include at least one number")
.regex(/[\W_]/, "Password must include at least one symbol"),
});
const state = reactive({
email: undefined,
password: undefined,
});
onMounted(async () => {
await nextTick();
if (loggedIn.value) navigateTo("/dashboard");
});
async function onSubmit(event) {
try {
loading.value = true;
await $fetch("/api/auth/login", {
method: "POST",
body: state,
});
return navigateTo("/dashboard");
} catch (error) {
toast.add({
title: "Error",
description: error.statusMessage,
color: "rose",
});
loading.value = false;
}
}
</script>
Can you please guide me to use this in the right way?
Similar to https://github.com/pilcrowOnPaper/oslo/blob/main/src/request/index.ts
We may want to use getRequestHost({ xForwardedHost: true })
(getRequestHost({ xForwardedHost: true }).)
Hi, thank you for your work on this module!
I'm quite new to Auth solutions, and saw projects use a state, or a baseUrl to protect against Cross Site Request Forgery (CSRF). I couldn't immediately see such a check implemented here, but I'm sure I'm missing something. Would you be just willing to lay out how this module handles CSRF?
Not a problem, but do you have any suggestions for blocking users by invalidating all tokens and preventing future sign-ins in a clean way?
Hello !
The project's working in local development, it's only messing up when deployed on Vercel :
https://talent-hub.fr
You can even try it on in the website.
The redirect_uri is set, client_id / secret as well, everything is working, you can connect with Discord, but after the redirection, nothing seems persisted :
const {loggedIn, user, session, clear, logout} = useUserSession()
The loggedIn is not set and the navbar is like for an authenticated user.
However, there's a page secured by an auth middleware :
pages/Profile.vue :
<template>
</template>
<script lang="ts" setup>
definePageMeta({
middleware: 'auth'
})
</script>
With the middleware :
export default defineNuxtRouteMiddleware(() => {
const { loggedIn } = useUserSession()
if (!loggedIn.value) {
return navigateTo('/')
}
})
When navigating, you can access it, and when you refresh, you could see that's the navbar is working as expected.
But once you comeback to the home page, and refresh, everything disappear, and when coming back again on profile page, everythink works.
It might be silly as I'm using nuxt-auth-utils
for the first time.
GitHub: https://github.com/TalentHubProject/monolith
(It's the talent-hub_website
nuxtjs3 project)
Regards;
Using the discrod provider I get this error in the terminal:
error: FetchError: [POST] "https://discord.com/api/oauth2/token": 400 Bad Request
I think this could be a problem:
Correct generated URL (from discord/developers )is:
https://discord.com/api/oauth2/authorize?client_id=CLIENTID&response_type=code&redirect_uri=https%3A%2F%2F199.254.176.236%3A3000%2Fapi%2Fauth%2Fdiscord&scope=identify
But from discord oauth is generated:
https://discord.com/oauth2/authorize?response_type=code&client_id=CLIENTID&redirect_uri=http://199.254.176.236:3000/api/auth/discord&scope=identify
Hey folks, thanks for the awesome library. My github oauth login is working on localhost:3000 just fine.
But when I deploy to CF Pages, nothing seems to happen when I use window.location.href = '/api/auth/github
Cloudflare seems to be returning 204 no content, it's like I'm never hitting this route. I can see the github token has never been accessed. Any ideas? Seems like infrastructure configuration, but I can't figure it out.
I am not using nuxt generate
, and my server API calls are working fine: https://minitinkertown.com/api/hello returns JSON
Hey,
are there plans to integrate facebook as a provider?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.