Documentation
¶
Overview ¶
Package identity holds the external identities Atlas users link and the delegated connections they authorize, and brokers the short-lived credentials those connections yield (ADR-0475 §3, §4).
Four concepts stay apart here. An Atlas principal is the user store's. An ExternalIdentity is one account at a provider, bound by issuer, tenant and stable subject — never by e-mail. A Connection is a principal's authorization for Atlas to act as that identity, through the Workers its owner allowed. Its credential, a refresh token, is a vault secret under a reserved name and never part of the connection record, a response, a log line or an event.
Design-time state: two sidecar stores owned by the run loop. Nothing here reaches the event log; the decisions made with a connection do, through the enforcement point.
Index ¶
- Constants
- Variables
- type Connection
- type ExternalIdentity
- type LinkOption
- type LinkRequest
- type Refresher
- type Secrets
- type Service
- func (s *Service) AccessToken(ctx context.Context, id string) (string, time.Time, error)
- func (s *Service) ConnectionFor(ownerID, provider, worker string) (Connection, bool, error)
- func (s *Service) ForOwner(userID string) ([]ExternalIdentity, []Connection)
- func (s *Service) ForgetAccessTokens()
- func (s *Service) Get(id string) (Connection, bool, error)
- func (s *Service) HandleMine(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleOfUser(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleRevoke(isAdmin func(*httpapi.Principal) bool) http.HandlerFunc
- func (s *Service) HandleSetWorkers(w http.ResponseWriter, r *http.Request)
- func (s *Service) Link(req LinkRequest) (Connection, error)
- func (s *Service) Linkable() []LinkOption
- func (s *Service) Revoke(id, by string, admin bool, reason string) (Connection, error)
- func (s *Service) SetAllowedWorkers(id, by string, workers []string) (Connection, error)
- func (s *Service) SetLinkable(options ...LinkOption)
- func (s *Service) Unrevoked(provider string) map[string]int
- type Stores
- type Tokens
Constants ¶
const ( StatusActive = "active" StatusReauthRequired = "reauth_required" StatusRevoked = "revoked" )
Connection states. They are authz's words, so the decision reads them as stored.
const AuthCodePKCE = "authorization_code_pkce"
AuthCodePKCE is how a delegated connection was authorized: the browser's authorization-code flow with PKCE, at the provider.
const ProviderMicrosoft = "microsoft"
Providers this build can link.
Variables ¶
var ( ErrNotFound = errors.New("identity: no such connection") ErrNotOwner = errors.New("identity: only the connection's owner may do that") ErrRevoked = errors.New("identity: the connection is revoked") ErrInactive = errors.New("identity: the connection is not active") ErrLinkedElsewhere = errors.New("identity: that external account is linked to another Atlas user") ErrUnknownWorker = errors.New("identity: no configured Worker has that name") ErrAmbiguous = errors.New("identity: more than one connection of the owner allows this Worker") // ErrInvalidGrant is a provider refusing a refresh token: revoked, expired, or // consent withdrawn. Only a person signing in again can mend it. ErrInvalidGrant = errors.New("identity: the provider refused the refresh token") )
Errors a caller distinguishes.
Functions ¶
This section is empty.
Types ¶
type Connection ¶
type Connection struct {
ID string `json:"id"`
OwnerID string `json:"ownerId"`
Provider string `json:"provider"`
IdentityID string `json:"identityId"`
// IdentityKey is the identity's Key, kept here so a decision can name whom it ran
// as without reading the identity store.
IdentityKey string `json:"identityKey"`
AuthMethod string `json:"authMethod"`
TenantID string `json:"tenantId,omitempty"`
Scopes []string `json:"scopes"`
// AllowedWorkers are the configured Workers that may act through this
// connection. Empty allows none: consenting to a sign-in is not consenting to
// every Worker.
AllowedWorkers []string `json:"allowedWorkers"`
Status string `json:"status"`
CreatedAt int64 `json:"createdAt"`
UpdatedAt int64 `json:"updatedAt"`
LastInteractiveAuthAt int64 `json:"lastInteractiveAuthAt"`
LastRefreshAt int64 `json:"lastRefreshAt,omitempty"`
RevokedAt int64 `json:"revokedAt,omitempty"`
RevokedBy string `json:"revokedBy,omitempty"`
RevokeReason string `json:"revokeReason,omitempty"`
}
Connection is the public half of a delegated connection.
type ExternalIdentity ¶
type ExternalIdentity struct {
ID string `json:"id"`
UserID string `json:"userId"`
Provider string `json:"provider"`
Issuer string `json:"issuer"`
TenantID string `json:"tenantId,omitempty"`
// Subject is the provider's stable identifier for the person: Microsoft's object
// id (oid), which unlike the pairwise sub identifies the person across
// applications.
Subject string `json:"subject"`
// Username and DisplayName are what the provider called the account at the last
// sign-in. Shown, never matched: an e-mail address can be reassigned.
Username string `json:"username,omitempty"`
DisplayName string `json:"displayName,omitempty"`
LinkedAt int64 `json:"linkedAt"`
LastAuthenticatedAt int64 `json:"lastAuthenticatedAt"`
}
ExternalIdentity is one account at a provider that an Atlas user linked.
func (ExternalIdentity) Key ¶
func (x ExternalIdentity) Key() string
Key is the identity's name in a decision's ExecutedAs.
type LinkOption ¶
LinkOption is one provider a person can link an account with.
type LinkRequest ¶
type LinkRequest struct {
UserID string
Provider string
Issuer string
TenantID string
Subject string
Username string
DisplayName string
Scopes []string
RefreshToken string
}
LinkRequest is a verified sign-in at a provider: every field comes out of an ID token whose signature, issuer, audience, nonce and lifetime were checked, or out of the token response that accompanied it.
type Refresher ¶
Refresher asks the provider for an access token with a refresh token. It returns ErrInvalidGrant (wrapped or not) when the provider refused the token itself.
type Secrets ¶
type Secrets interface {
Get(name string) (string, bool, error)
Set(name, value string) (vault.Meta, error)
Delete(name string) error
}
Secrets is the vault, as this service uses it. Every call is made on the run loop.
type Service ¶
type Service struct {
// Limits are the installation's resource budgets. New sets them to
// [limits.Default]; the server overwrites them with its own.
Limits limits.Limits
// contains filtered or unexported fields
}
Service is the identity area. Build it with New.
func New ¶
func New(loop *runloop.Loop, stores *Stores, secrets Secrets, refresh Refresher, workerKnown func(name string) bool, now func() time.Time) *Service
New builds the service. refresh asks the provider for tokens; workerKnown says whether a configured Worker of that name exists (it is called on the loop).
func (*Service) AccessToken ¶
AccessToken is the credential broker: a short-lived access token for an active connection. Atlas is the only holder of the refresh token, refreshes one connection at a time, and writes a rotated refresh token to the vault before releasing the access token that came with it — so a crash can lose an access token, never leave the vault holding a superseded refresh token. A connection revoked while its refresh was in flight keeps its revocation: the rotated token is not written back and nothing is released.
The caller is the enforcement point, after a durably recorded Permit; nothing else may call this.
func (*Service) ConnectionFor ¶
func (s *Service) ConnectionFor(ownerID, provider, worker string) (Connection, bool, error)
ConnectionFor chooses the connection a Worker acts through for an owner: their one connection of that provider that allows the Worker. None is "no connection"; two is ambiguous, and ambiguity chooses nothing rather than guessing whose account acts. Revoked and inactive connections are returned when they are the only candidate, so the decision can say why it refused.
func (*Service) ForOwner ¶
func (s *Service) ForOwner(userID string) ([]ExternalIdentity, []Connection)
ForOwner lists a user's identities and connections, newest first.
func (*Service) ForgetAccessTokens ¶ added in v0.21.0
func (s *Service) ForgetAccessTokens()
ForgetAccessTokens drops every cached access token, so the next release for any connection asks the provider again, under whatever app registration is configured by then. The server calls it whenever linking is set up, changed or removed: a token obtained under the previous setup must not be handed out for up to an hour after it. A release already in flight is the check-then-act window §13 accepts for a revocation, and ends the same way: the next one is decided afresh.
func (*Service) Get ¶
func (s *Service) Get(id string) (Connection, bool, error)
Get reads one connection.
func (*Service) HandleMine ¶
func (s *Service) HandleMine(w http.ResponseWriter, r *http.Request)
HandleMine lists the caller's own identities and connections.
func (*Service) HandleOfUser ¶
func (s *Service) HandleOfUser(w http.ResponseWriter, r *http.Request)
HandleOfUser lists one user's identities and connections, for an administrator answering "what can act as this person".
func (*Service) HandleRevoke ¶
HandleRevoke withdraws a connection. Its owner may, and so may an administrator.
func (*Service) HandleSetWorkers ¶
func (s *Service) HandleSetWorkers(w http.ResponseWriter, r *http.Request)
HandleSetWorkers replaces the Workers allowed to act through a connection. Only the owner may: it is their consent.
func (*Service) Link ¶
func (s *Service) Link(req LinkRequest) (Connection, error)
Link records a verified sign-in: the identity, and a connection authorized by it. Linking an account already linked to this user re-authenticates its connection; one linked to somebody else is refused. The credential is stored before the connection says it is active, so an active connection always has one.
func (*Service) Linkable ¶ added in v0.21.0
func (s *Service) Linkable() []LinkOption
Linkable is what SetLinkable last said, never nil.
func (*Service) Revoke ¶
Revoke withdraws a connection for good: the credential is deleted and the record says who withdrew it and why. Its owner may revoke it, and so may an administrator — taking access away is never a person's alone to block.
func (*Service) SetAllowedWorkers ¶
func (s *Service) SetAllowedWorkers(id, by string, workers []string) (Connection, error)
SetAllowedWorkers replaces the Workers that may act through a connection. Only its owner consents to that: an administrator who could add a Worker here could have any Worker act as anybody.
func (*Service) SetLinkable ¶
func (s *Service) SetLinkable(options ...LinkOption)
SetLinkable says which providers this server links accounts with. The server sets it while it is built and again whenever an administrator sets up or removes linking; none clears it.
type Stores ¶
type Stores struct {
// contains filtered or unexported fields
}
Stores are the two sidecar stores.
func OpenStores ¶
OpenStores opens the stores under dir.