| 1 |
|
package authn |
| 2 |
|
|
| 3 |
|
import ( |
| 4 |
|
"context" |
| 5 |
|
"encoding/base64" |
| 6 |
|
"fmt" |
| 7 |
|
"strings" |
| 8 |
|
|
| 9 |
|
"sourcecraft.dev/bigbes/sr-ht-core/auth" |
| 10 |
|
) |
| 11 |
|
|
| 12 |
|
// ResolveGRPCAuth resolves the caller from a remotesapi gRPC "authorization" |
| 13 |
|
// metadata header, dispatching on its scheme exactly as the dolt client sends |
| 14 |
|
// it (see dolt's grpc_dial_provider): |
| 15 |
|
// |
| 16 |
|
// - "Basic <base64(user:pass)>" → ResolveBasic (meta personal access token); |
| 17 |
|
// - "Bearer <jwt>" → ResolveDoltJWT (dolt keypair EdDSA JWT); |
| 18 |
|
// - empty → (nil, nil): an anonymous request, which is |
| 19 |
|
// valid for public clones. |
| 20 |
|
// |
| 21 |
|
// A malformed or unsupported header is a permanent rejection wrapping |
| 22 |
|
// ErrInvalidToken. keys is only consulted for the Bearer path. |
| 23 |
9 |
func ResolveGRPCAuth(ctx context.Context, authorizationHeader, expectedAud string, keys KeyStore) (*auth.AuthContext, error) { |
| 24 |
9 |
if authorizationHeader == "" { |
| 25 |
1 |
return nil, nil // anonymous |
| 26 |
1 |
} |
| 27 |
|
|
| 28 |
8 |
scheme, value, ok := strings.Cut(authorizationHeader, " ") |
| 29 |
8 |
if !ok || value == "" { |
| 30 |
2 |
return nil, fmt.Errorf("%w: malformed authorization header", ErrInvalidToken) |
| 31 |
2 |
} |
| 32 |
|
|
| 33 |
6 |
switch strings.ToLower(scheme) { |
| 34 |
4 |
case "basic": |
| 35 |
4 |
raw, err := base64.StdEncoding.DecodeString(value) |
| 36 |
4 |
if err != nil { |
| 37 |
1 |
return nil, fmt.Errorf("%w: Basic credentials are not valid base64: %v", ErrInvalidToken, err) |
| 38 |
1 |
} |
| 39 |
3 |
username, password, ok := strings.Cut(string(raw), ":") |
| 40 |
3 |
if !ok { |
| 41 |
1 |
return nil, fmt.Errorf("%w: Basic credentials missing ':' separator", ErrInvalidToken) |
| 42 |
1 |
} |
| 43 |
2 |
return ResolveBasic(ctx, username, password) |
| 44 |
1 |
case "bearer": |
| 45 |
1 |
return ResolveDoltJWT(ctx, value, expectedAud, keys) |
| 46 |
1 |
default: |
| 47 |
1 |
return nil, fmt.Errorf("%w: unsupported authorization scheme %q", ErrInvalidToken, scheme) |
| 48 |
|
} |
| 49 |
|
} |