feat: name the missing scope or permission in 403 bodies
A refusal by user.scope:<scope> now answers 403 with missing_scope and a refusal by user.permission:<code> with missing_permission, next to the unchanged error (true) and message, so a plain-HTTP script or a personal token client learns what it lacks. user_me says it needs scope read.
This commit is contained in:
@@ -86,14 +86,15 @@ func (g *APITokenGuard) WriteUnauthorized(w http.ResponseWriter, _ error) {
|
||||
wire.WriteJSON(w, http.StatusUnauthorized, map[string]any{"error": true, "message": "Invalid token."})
|
||||
}
|
||||
|
||||
// Scope requires one delegated scope from the shared token credential.
|
||||
// Scope requires one delegated scope from the shared token credential. A
|
||||
// refusal answers 403 with error, message and missing_scope naming the scope.
|
||||
func Scope(scope string) pact.Middleware {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
credential, _ := bouncer.Credential(r.Context())
|
||||
token, ok := credential.(*models.APIToken)
|
||||
if !ok || token == nil || !token.HasScope(scope) {
|
||||
wire.WriteJSON(w, http.StatusForbidden, map[string]any{"error": true, "message": "Missing required scope: " + scope})
|
||||
wire.WriteJSON(w, http.StatusForbidden, map[string]any{"error": true, "message": "Missing required scope: " + scope, "missing_scope": scope})
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
@@ -103,12 +104,14 @@ func Scope(scope string) pact.Middleware {
|
||||
|
||||
// Permission requires one current user-group permission. Tokens never carry
|
||||
// a permission snapshot: revoking the user's grant takes effect immediately.
|
||||
// A refusal answers 403 with error, message and missing_permission naming the
|
||||
// code.
|
||||
func Permission(code string) pact.Middleware {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
principal, _ := bouncer.User(r.Context())
|
||||
if !cabana.Allows(principal, []string{code}) {
|
||||
wire.WriteJSON(w, http.StatusForbidden, map[string]any{"error": true, "message": "Forbidden."})
|
||||
wire.WriteJSON(w, http.StatusForbidden, map[string]any{"error": true, "message": "Forbidden.", "missing_permission": code})
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
|
||||
Reference in New Issue
Block a user