Edit this page

Authentication

In principle, you can use Vike with any authentication tool such as:

pageContext.user

You can make information about the authenticated user available to your UI components and Vike hooks by defining a custom property pageContext.user in +onCreatePageContext.server.js.

// pages/+onCreatePageContext.server.ts
// Environment: server
 
import type { PageContextServer } from 'vike/types'
 
// This hook is called upon new incoming HTTP requests
export async function onCreatePageContext(pageContext: PageContextServer) {
  // vike-server exposes the request object at pageContext.runtime.req
  const { req } = pageContext.runtime
 
  // Authentication middlewares (e.g. Passport.js and Grant) usually set req.user
  // to expose information about the logged-in user.
  const { user } = req
  // Or, when using a third-party authentication provider (e.g. Auth0):
  const user = await someAuthApi.getUser(req.headers)
 
  // Select the properties you want to make available everywhere
  pageContext.user = {
    firstName: user.firstName,
    email: user.email
  }
}
 
declare global {
  namespace Vike {
    interface PageContext {
      user?: {
        firstName: string
        email: string
      }
    }
  }
}
// pages/+config.ts
 
import type { Config } from 'vike/types'
 
export default {
  // Make pageContext.user available on the client-side
  passToClient: [{
    prop: 'user',
    // Send it only once: upon client-side navigation re-use the initial pageContext.user value
    once: true
  }]
} satisfies Config

See also: API > passToClient

You can access pageContext in any UI component and Vike hook — see API > pageContext.

If you use vike-server, you can access the request object at pageContext.runtime.req.user (see pageContext.runtime).

If you don't use vike-server, you can define a custom property pageContext.req at renderPage():

// server/index.ts
 
import { renderPage } from 'vike/server'
 
// Vike server middleware
app.get('*', async (req: Request, res: Response) => {
  const pageContextInit = {
    urlOriginal: req.url,
    // Make the request object available at pageContext.req
    req
  }
  const pageContext = await renderPage(pageContextInit)
  // ...
})
 
declare global {
  namespace Vike {
    interface PageContextServer {
      req: Request
    }
  }
}

SSG

SSG apps don't have a server and, therefore, a custom property such as pageContext.user cannot be defined on the server-side.

Instead, you can use +onCreateGlobalContext.client.js:

// pages/+onCreateGlobalContext.client.js
// Environment: client
 
export async function onCreateGlobalContext(globalContext) {
  // Fetch user information from your backend (or an authentication SaaS platform)
  const user = await fetchUseInfo()
  globalContext.user = user
}

You can then use pageContext.globalContext and/or getGlobalContext() to access globalContext.user anywhere.

Login flow

By using +guard with throw redirect() or throw render() you can:

  • Implement login flows.
  • Protect private pages from unprivileged access.
// /pages/admin/+guard.js
 
import { render, redirect } from 'vike/abort'
 
export const guard = (pageContext) => {
  const { user } = pageContext
  if (user === null) {
    // Render the login page while preserving the URL. (This is novel technique
    // which we explain down below.)
    throw render('/login')
    /* The more traditional way, redirect the user:
    throw redirect('/login')
    */
  }
  if (user.role !== 'admin') {
    // Render the error page and show message to the user
    throw render(403, 'Only admins are allowed to access this page.')
  }
}

Using render('/login') instead of redirect('/login') allows the URL to be preserved during the entire login flow:

  1. Unauthenticated user goes to URL /admin and sees the login page. (URL is /admin.)
  2. User fills the sign-in form and successfully logs in. (URL is still /admin.)
  3. Reload the page, the user now sees the admin page. (URL is still /admin.)

See example.

You can also define a guard() hook that applies to multiple pages:

// /pages/+guard.js
 
// This guard() hook applies to all pages: the file is located at /pages/+guard.js
// and therefore applies to all /pages/**/+Page.js
 
import { render } from 'vike/abort'
 
export const guard = (pageContext) => {
  if (!pageContext.user) {
    throw render('/login')
  }
}

See also