Skip to content

feat(#964): Add Hooks provider#1062

Open
phoenix-ru wants to merge 16 commits into
mainfrom
feat/964-add-hooks-provider
Open

feat(#964): Add Hooks provider#1062
phoenix-ru wants to merge 16 commits into
mainfrom
feat/964-add-hooks-provider

Conversation

@phoenix-ru

@phoenix-ru phoenix-ru commented Oct 17, 2025

Copy link
Copy Markdown
Member

🔗 Linked issue

Closes #964

❓ Type of change

  • 📖 Documentation (updates to the documentation, readme or JSdoc annotations)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • 👌 Enhancement (improving an existing functionality like performance)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

📚 Description

📝 Checklist

  • I have linked an issue or discussion.
  • I have added tests (if possible).
  • I have updated the documentation accordingly.

@pkg-pr-new

pkg-pr-new Bot commented Oct 17, 2025

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@sidebase/nuxt-auth@1062

commit: 62a5fc2

@pkg-pr-new

pkg-pr-new Bot commented Oct 17, 2025

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@sidebase/nuxt-auth@1062

commit: 0c38105

@phoenix-ru phoenix-ru marked this pull request as ready for review December 18, 2025 17:24
docs: improve documentation
Comment thread docs/guide/hooks/adapter.md Outdated
Comment on lines +36 to +49
## `createRequest(data, authState, nuxt)`

Prepare data for the fetch call.

Must return either an object conforming to:

```ts
interface CreateRequestResult {
// Path to the endpoint
path: string
// Request: body, headers, etc.
request: NitroFetchOptions
}
```

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you decide to abstract the returns of the hooks provider? While I think it is a good step forward compared to the current local-provider I think it could still limit you if you e.g. want to switch from ofetch to axios under the hood.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it would ever make sense for us to switch the underlying fetch library to something outside Nuxt ecosystem. At the moment ofetch is the default library of Nuxt, therefore it is being used

Comment thread docs/guide/hooks/adapter.md Outdated
Comment on lines +53 to +55
### `authState` argument

This argument gives you access to the state of the module, allowing to read or modify session data or tokens.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a schema for this or this is e.g. based off the session object you can provide via the nuxt config?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we do! I updated the documentation for it to point to useAuthState:

image

Comment thread docs/guide/hooks/adapter.md Outdated

This argument gives you access to the state of the module, allowing to read or modify session data or tokens.

### `nuxt` argument

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we call this nuxtApp like Nuxt also does in the docs you linked below?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed

Comment thread docs/guide/hooks/adapter.md Outdated
Handle the response and optionally instruct the module how to update state.

May return:
* `false` — stop further processing (module will not update auth state).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this false act the same way as the return false from the createRequest hook?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, exactly the same - the meaning of false is to stop execution without an error

* `ResponseAccept` object — instruct the module what to set in `authState` (see below).
* Throw an `Error` to propagate a failure.

The `response` argument is the [`ofetch` raw response](https://github.com/unjs/ofetch?tab=readme-ov-file#-access-to-raw-response) that the module uses as well. `response._data` usually contains parsed body.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What else can response._data contain if not the parsed body? E.g. do we do error handling here or just return whatever was in the body?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do error handling for the request:

let response: FetchResponse<T>
try {
response = await _fetchRaw<T>(nuxt, createRequestResult.path, createRequestResult.request)
}
catch (e) {
if (hooks.onError) {
await hooks.onError({
error: transformToError(e),
requestData: createRequestResult,
}, authState, nuxt)
}
// Do not proceed when error occurred
return
}

But thinking a bit more, we are silently consuming the error which is not very good for this usecase:

catch (error) {
let errorMessage = `${ERROR_PREFIX} Error while requesting ${joinedPath}.`
if (runtimeConfig.public.auth.provider.type === 'authjs') {
errorMessage += ' Have you added the authentication handler server-endpoint `[...].ts`? Have you added the authentication handler in a non-default location (default is `~/server/api/auth/[...].ts`) and not updated the module-setting `auth.basePath`?'
}
errorMessage += ' Error is:'
console.error(errorMessage)
console.error(error)
throw new FetchConfigurationError(
'Runtime error, check the console logs to debug, open an issue at https://github.com/sidebase/nuxt-auth/issues/new/choose if you continue to have this problem'
)
}
}

It was done like so to avoid leaking exact error details (which might not be caught by consuming code and passed to the app side -> viewable by the frontend). I will consider if this could be improved, there's an external PR for better error handling:
#1069

Comment on lines +24 to +33
onResponse(response) {
// Backend returns { access: 'xxx', refresh: 'yyy', user: {...} }
const body = response._data
// Default to `undefined` to not reset the tokens and session (but you may want to reset it)
return {
token: body?.access ?? undefined,
refreshToken: body?.refresh ?? undefined,
session: body?.user ?? undefined,
}
},

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these hooks promises? E.g. if I wanted to do another DB or API call here, would the hooks provider support this?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the hooks are awaitable:

const signInResponseAccept = await Promise.resolve(hooks.onResponse(response, authState, nuxt))

}
},

onResponse(response) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I return false from the getSession.createRequest hook is this response:

  • false
  • null / undefined
  • onResponse is never called

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The onResponse is never called:

const createRequestResult = await Promise.resolve(hooks.createRequest({ credentials, options }, authState, nuxt))
if (createRequestResult === false) {
return
}

Comment thread docs/guide/hooks/adapter.md Outdated

This argument gives you access to the state of the module, allowing to read or modify session data or tokens.

### `nuxt` argument

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to add a guide on how you can use this to e.g. access runtimeConfig variables? I think this could be a pretty common workflow.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do, thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RFC: Hooks for the local provider

2 participants