IsAuthorized validates a given Coder API key and returns the user ID to which it belongs (if valid). SECURITY: when in.KeyId is set (the "delegated" path), this method trusts the caller's claim of identity and skips the key-secret check. This is safe only because the DRPCServer is reachable solely
(ctx context.Context, in *proto.IsAuthorizedRequest)
| 574 | // |
| 575 | // TODO: replace with logic from [httpmw.ExtractAPIKey]. |
| 576 | func (s *Server) IsAuthorized(ctx context.Context, in *proto.IsAuthorizedRequest) (*proto.IsAuthorizedResponse, error) { |
| 577 | //nolint:gocritic // AIBridged has specific authz rules. |
| 578 | ctx = dbauthz.AsAIBridged(ctx) |
| 579 | |
| 580 | var ( |
| 581 | keyID string |
| 582 | keySecret string |
| 583 | // delegated requests skip the secret check: the caller never |
| 584 | // has the secret. Trust is established at the in-process |
| 585 | // transport boundary, not in this RPC. |
| 586 | delegated bool |
| 587 | ) |
| 588 | switch { |
| 589 | case in.GetKey() != "" && in.GetKeyId() != "": |
| 590 | return nil, ErrAmbiguousAuth |
| 591 | case in.GetKeyId() != "": |
| 592 | keyID = in.GetKeyId() |
| 593 | delegated = true |
| 594 | default: |
| 595 | var err error |
| 596 | keyID, keySecret, err = httpmw.SplitAPIToken(in.GetKey()) |
| 597 | if err != nil { |
| 598 | return nil, ErrInvalidKey |
| 599 | } |
| 600 | } |
| 601 | |
| 602 | // Key exists. |
| 603 | key, err := s.store.GetAPIKeyByID(ctx, keyID) |
| 604 | if err != nil { |
| 605 | s.logger.Warn(ctx, "failed to retrieve API key by id", slog.F("key_id", keyID), slog.Error(err)) |
| 606 | return nil, ErrUnknownKey |
| 607 | } |
| 608 | |
| 609 | // Key has not expired. |
| 610 | now := dbtime.Now() |
| 611 | if key.ExpiresAt.Before(now) { |
| 612 | return nil, ErrExpired |
| 613 | } |
| 614 | |
| 615 | // Key secret matches (skipped for delegated callers). |
| 616 | if !delegated && !apikey.ValidateHash(key.HashedSecret, keySecret) { |
| 617 | return nil, ErrInvalidKey |
| 618 | } |
| 619 | |
| 620 | // User exists. |
| 621 | user, err := s.store.GetUserByID(ctx, key.UserID) |
| 622 | if err != nil { |
| 623 | s.logger.Warn(ctx, "failed to retrieve API key user", slog.F("key_id", keyID), slog.F("user_id", key.UserID), slog.Error(err)) |
| 624 | return nil, ErrUnknownUser |
| 625 | } |
| 626 | |
| 627 | // User is active, not deleted, and not a system user. |
| 628 | if user.Deleted { |
| 629 | return nil, ErrDeletedUser |
| 630 | } |
| 631 | if user.Status != database.UserStatusActive { |
| 632 | return nil, ErrInactiveUser |
| 633 | } |