v0.7.4 beta
Back-end UI Framework

for feature-rich, secure, and fast web apps in Go

Docs

Authentication

For protected resources, control authentication and authorization directly in the App handler.


Access Control

Example:

router.Use(doors.ServeApp(
  func(a doors.ModelRouter[Path], r doors.RModel[Path]) doors.ModelRoute {
    c, err := r.GetCookie("session")
    if err != nil {
      // show unauthorized page in detached (no path sync) mode
      return a.Reroute(UnauthorizedPath{}, true)
    }

    session, ok := db.Get(c.Value)
    if !ok {
      return a.Reroute(UnauthorizedPath{}, true)
    }

    return a.App(&Admin{})
  },
))

doors.RModel provides access to cookies, headers, and the requested Path Model.

There is no need to recheck cookies or headers in event handlers, since they are already scoped to the session and page instance.

Important: Never rely only on a cookie value — always validate against session storage.


When to Check Authorization Besides the App Handler

If user access to certain actions or views can be revoked, you should:

  • Verify user view permissions during render to ensure the user cannot access previously available views through dynamic navigation.
  • Verify user write permissions in transactions to ensure that even if permissions are revoked after rendering, data integrity and security are maintained.

These checks prevent stale or cached views from exposing restricted content after permissions change.


Session Management

The framework internally manages sessions for pages, content, and event handling.
By default, a session persists as long as at least one model instance remains active.

When implementing your own authentication, ensure your application’s session does not outlive the framework’s session.
Otherwise, users may appear logged out while their open tabs still have access.


1. Control Expiration in Login Handler

templ (l *loginFragment) Render() {
  @doors.ASubmit[loginData]{
    On: func(ctx context.Context, r doors.RForm[loginData]) bool {
      // validate credentials

      sessionDuration := 24 * time.Hour
      session := db.CreateSession(r.Data, sessionDuration)
      r.SetCookie(&http.Cookie{
        Name:     "session",
        Value:    session.Token,
        Expires:  time.Now().Add(sessionDuration),
        Path:     "/",
        HttpOnly: true,
      })

      // align internal session lifetime with cookie
      doors.SessionExpire(ctx, sessionDuration)

      // reload to initialize private instance
      r.After(doors.ActionOnlyLocationReload())
      return true
    },
  }
  <form>...</form>
}

2. End Session on Logout

Destroy all active instances when logging out.

templ logout() {
  @doors.AClick{
    On: func(ctx context.Context, r doors.REvent[doors.PointerEvent]) bool {
      // end doors session
      defer doors.SessionEnd(ctx)

      // clear cookies
      r.SetCookie(&http.Cookie{
        Name:   "session",
        Path:   "/",
        MaxAge: -1,
      })

      // remove session record
      db.Sessions.Remove(h.session.Token)
      return true
    },
  }
  <button>Log Out</button>
}

Note: Ending the session reloads all pages.
This might occur before cookies are cleared.
To ensure consistency, always use session storage as the single source of truth for authentication.