v0.5.7 beta
Back-end UI Framework

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

Docs

Authentification

For protected resources, you need to control authentication & authorization in the page handler.

Access Control

Example:


router.Use(doors.ServePage(
  // page handler function
  func (p doors.PageRouter[Path], r doors.RPage[Path]) doors.PageRoute {
  	c, err := r.GetCookie("session")
    if err != nil {
      // show unauthorized page in detached (without path sync) mode 
    	return p.Reroute(UnauthorizedPath{}, true)
    }
    session, ok := db.Get(c.Value)
    if !ok  {
    	return p.Reroute(UnauthorizedPath{}, true)
    }
  	return p.Page(&Admin{})
	},
))

doors.RPage gives you access to cookies, headers, and the requested path model.

There is no need to check cookies/headers in event handlers, because they are already scoped to the session and page instance.

❗ Don’t rely only on the cookie value; always implement session storage

⚠️ When to check authorization besides the page handler

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

  • Verify user view permissions during render to ensure that the user can’t access previously available views with dynamic navigation.
  • Verify user write permissions in the transactions to ensure that even if the permission is revoked after rendering, you are still safe.

Session Management

The framework has internal session mechanics to serve pages, content, and handle events. By default, an internal session lasts until at least one page instance remains alive.

However, when you implement your own authentication, ensure that the framework’s session does not outlive yours. Otherwise, you may leave the users in a situation where they are logged out, but opened tabs still have access to the private space.

1. Control expiration in login handler

templ (l *loginFragment) Render() {
  // login form handler
	@doors.ASubmit[loginData]{
	  /* setup */
		On: func(ctx context.Context, r doors.RForm[loginData]) bool {
		  /*  check login data */
		  
		  sessionDuration := time.Hour * 24
			session := db.CreateSession(r.Data, sessionDuration)
			r.SetCookie(&http.Cookie{
				Name:     "session",
				Value:    session.Token,
				// set expiration
				Expires:  time.Now().Add(sessionDuration),
				Path:     "/",
				HttpOnly: true,
			})
	
			// ✅ set internal session expiration to not outlive cookies
			doors.SessionExpire(ctx, sessionDuration)
			
			// reload after request to initiate instance with private space
			r.After(doors.ActionOnlyLocationReload())
			return true
		},
	}
	<form>
		/* ... */
	</form>
}

2. End session on log-out

When the user logs out, destroy all active instances by ending the session.

templ logout() {
		@doors.AClick{
          On: func(ctx context.Context, r doors.REvent[doors.PointerEvent]) bool {
             // ✅ end doors session to ensure no active pages left
            defer doors.SessionEnd(ctx)
            
            // clean cookies
            r.SetCookie(&http.Cookie{
              Name:   "session",
              Path:   "/",
              MaxAge: -1,
            })
            // remove session entry
            db.Sessions.Remove(h.session.Token)
           
            return true
          },
        }
		<button>Log Out</button>

}

⚠️ Ending the session causes pages to reload. In theory, it could happen before the browser receives a response and clears the cookies. To be on the safe side, rely on session storage (remove on logout, check in the page handler).