package auth import ( "errors" "net/http" "github.com/bluesky-social/indigo/atproto/auth/oauth" "github.com/bluesky-social/indigo/atproto/syntax" ) // ErrNoSession is returned by ResumeSession when no auth cookie is present. // Other features should check for this with errors.Is and redirect to /signin. var ErrNoSession = errors.New("auth: no session") // IdentityInfo holds the resolved viewer identity. // Use ResolveIdentity in handlers that support both ATProto and local users. type IdentityInfo struct { DID string // ATProto DID (did:plc:...) or local ID (local_xxx) IsLocal bool // true when the user is a local-only account IsAuth bool // true when the user is authenticated (either type) } // ResolveIdentity checks the ATProto session cookie first, then the local // cookie. Returns the identity without writing any redirect headers. // Use this in handlers that can serve both ATProto and local users. func (h *Handlers) ResolveIdentity(r *http.Request) IdentityInfo { didStr, sid := h.Sessions.Get(r) if didStr != "" && sid != "" { return IdentityInfo{DID: didStr, IsLocal: false, IsAuth: true} } localID := h.Sessions.GetLocal(r) if localID != "" { return IdentityInfo{DID: localID, IsLocal: true, IsAuth: true} } return IdentityInfo{} } // ResumeSession reads the session cookie and asks indigo to resume the OAuth // session for the resulting (DID, SessionID). Returns ErrNoSession when no // cookie is present so callers can distinguish "logged out" from "bad token". // // Use this from any authenticated handler. Example: // // did, sess, err := h.Auth.ResumeSession(r) // if err != nil { http.Redirect(w, r, "/signin", http.StatusFound); return } func (h *Handlers) ResumeSession(r *http.Request) (syntax.DID, *oauth.ClientSession, error) { didStr, sid := h.Sessions.Get(r) if didStr == "" || sid == "" { return "", nil, ErrNoSession } did, err := syntax.ParseDID(didStr) if err != nil { return "", nil, err } sess, err := h.OAuth.ResumeSession(r.Context(), did, sid) if err != nil { return did, nil, err } return did, sess, nil } // RequireSession is a small helper that redirects to /signin?next= if // no session is present, otherwise returns the resumed indigo session. // // did, sess, ok := h.Auth.RequireSession(w, r) // if !ok { return } func (h *Handlers) RequireSession(w http.ResponseWriter, r *http.Request) (syntax.DID, *oauth.ClientSession, bool) { did, sess, err := h.ResumeSession(r) if err != nil { http.Redirect(w, r, "/signin?next="+r.URL.Path, http.StatusFound) return "", nil, false } return did, sess, true }