package api import ( "fmt" "net/http" "github.com/sirupsen/logrus" "github.com/supabase/auth/internal/models" "github.com/supabase/auth/internal/storage" ) type LogoutBehavior string const ( LogoutGlobal LogoutBehavior = "global" LogoutLocal LogoutBehavior = "local" LogoutOthers LogoutBehavior = "others" ) // Logout is the endpoint for logging out a user and thereby revoking any refresh tokens func (a *API) Logout(w http.ResponseWriter, r *http.Request) error { ctx := r.Context() db := a.db.WithContext(ctx) scope := LogoutGlobal if r.URL.Query() != nil { switch r.URL.Query().Get("scope") { case "", "global": scope = LogoutGlobal case "local": scope = LogoutLocal case "others": scope = LogoutOthers default: return badRequestError(ErrorCodeValidationFailed, fmt.Sprintf("Unsupported logout scope %q", r.URL.Query().Get("scope"))) } } s := getSession(ctx) u := getUser(ctx) err := db.Transaction(func(tx *storage.Connection) error { if terr := models.NewAuditLogEntry(r, tx, u, models.LogoutAction, "", nil); terr != nil { return terr } if s == nil { logrus.Infof("user has an empty session_id claim: %s", u.ID) } else { //exhaustive:ignore Default case is handled below. switch scope { case LogoutLocal: return models.LogoutSession(tx, s.ID) case LogoutOthers: return models.LogoutAllExceptMe(tx, s.ID, u.ID) } } // default mode, log out everywhere return models.Logout(tx, u.ID) }) if err != nil { return internalServerError("Error logging out user").WithInternalError(err) } w.WriteHeader(http.StatusNoContent) return nil }