Documentation
¶
Overview ¶
Package idp is external identity: the providers an administrator connects, redirect sign-in through them, linking their identities to accounts, the groups they sync, and SCIM provisioning (R-043, R-045, R-047, R-048, R-050).
Every surface calls this — the HTTP API, and through it the console and CLI — so none of them holds a rule the others lack (R-261). Adapters authenticate; everything that decides anything is here (R-044).
Index ¶
- Constants
- func ApplyPatch(resource map[string]any, ops []PatchOp) error
- func Digest(v string) string
- func Paging(startIndex, count int) (offset, limit int)
- func SafeNext(next string) string
- type Completed
- type Filter
- type Kind
- type Origin
- type PatchOp
- type PatchRequest
- type Problem
- type ProviderInput
- type ProviderView
- type PublicProvider
- type Revocation
- type SCIMContext
- type SCIMError
- type Service
- func (s *Service) Adapter(ctx context.Context, providerID string) (api.IdentityAdapter, state.IdentityProvider, error)
- func (s *Service) Callback(ctx context.Context, providerID string, params url.Values, o Origin) string
- func (s *Service) CheckProvider(ctx context.Context, providerID string) error
- func (s *Service) Complete(ctx context.Context, code, bind string, o Origin, userAgent, ip string) (Completed, error)
- func (s *Service) CreateProvider(ctx context.Context, p authz.Principal, in ProviderInput, o Origin) (ProviderView, error)
- func (s *Service) DeleteProvider(ctx context.Context, p authz.Principal, providerID string) error
- func (s *Service) DisableSCIM(ctx context.Context, p authz.Principal, providerID string) error
- func (s *Service) Endpoints(o Origin, providerID string) api.Endpoints
- func (s *Service) FailureMessage(ctx context.Context, flowID string) (Problem, bool, error)
- func (s *Service) IdentitiesFor(ctx context.Context, userID string) ([]state.Identity, error)
- func (s *Service) KindInfos() []api.KindInfo
- func (s *Service) LinkGroup(ctx context.Context, p authz.Principal, groupID, syncedGroupID string) error
- func (s *Service) LinkIdentity(ctx context.Context, p authz.Principal, userID, providerID, externalID string, ...) (string, error)
- func (s *Service) ListProviders(ctx context.Context, o Origin) ([]ProviderView, error)
- func (s *Service) Options(ctx context.Context) (SignInOptions, error)
- func (s *Service) PasswordSignInAllowed(ctx context.Context) error
- func (s *Service) Provider(ctx context.Context, o Origin, providerID string) (ProviderView, error)
- func (s *Service) RotateSCIMToken(ctx context.Context, p authz.Principal, providerID string) (secret.Value, error)
- func (s *Service) SCIMAuthenticate(ctx context.Context, bearer string, o Origin) (SCIMContext, error)
- func (s *Service) SCIMBaseURL(o Origin) string
- func (s *Service) SCIMCreateGroup(ctx context.Context, c SCIMContext, body []byte) (map[string]any, error)
- func (s *Service) SCIMCreateUser(ctx context.Context, c SCIMContext, body []byte) (map[string]any, error)
- func (s *Service) SCIMDeleteGroup(ctx context.Context, c SCIMContext, groupID string) error
- func (s *Service) SCIMDeleteUser(ctx context.Context, c SCIMContext, userID string) error
- func (s *Service) SCIMGetGroup(ctx context.Context, c SCIMContext, groupID string, excludeMembers bool) (map[string]any, error)
- func (s *Service) SCIMGetUser(ctx context.Context, c SCIMContext, userID string) (map[string]any, error)
- func (s *Service) SCIMListGroups(ctx context.Context, c SCIMContext, filter string, startIndex, count int, ...) (map[string]any, error)
- func (s *Service) SCIMListUsers(ctx context.Context, c SCIMContext, filter string, startIndex, count int) (map[string]any, error)
- func (s *Service) SCIMPatchGroup(ctx context.Context, c SCIMContext, groupID string, body []byte) error
- func (s *Service) SCIMPatchUser(ctx context.Context, c SCIMContext, userID string, body []byte) (map[string]any, error)
- func (s *Service) SCIMReplaceGroup(ctx context.Context, c SCIMContext, groupID string, body []byte) (map[string]any, error)
- func (s *Service) SCIMReplaceUser(ctx context.Context, c SCIMContext, userID string, body []byte) (map[string]any, error)
- func (s *Service) SCIMResourceTypes(c SCIMContext) map[string]any
- func (s *Service) SCIMSchemas(c SCIMContext) map[string]any
- func (s *Service) SCIMServiceProviderConfig(c SCIMContext) map[string]any
- func (s *Service) ServiceMetadata(ctx context.Context, o Origin, providerID string) (*api.Metadata, error)
- func (s *Service) SessionLifetime(ctx context.Context, providerID string) (time.Duration, error)
- func (s *Service) Start(ctx context.Context, req StartRequest) (Started, error)
- func (s *Service) TestResult(ctx context.Context, p authz.Principal, providerID, flowID string) (TestReport, error)
- func (s *Service) UnlinkGroup(ctx context.Context, p authz.Principal, groupID, syncedGroupID string) error
- func (s *Service) UnlinkIdentity(ctx context.Context, p authz.Principal, userID, providerID, externalID string) error
- func (s *Service) UpdateProvider(ctx context.Context, p authz.Principal, providerID string, in ProviderInput, ...) (ProviderView, error)
- func (s *Service) ValidatePolicy(ctx context.Context, before, after bool) error
- type SignInOptions
- type SignInOutcome
- type StartRequest
- type Started
- type SubjectView
- type TestReport
Constants ¶
const ( SchemaUser = "urn:ietf:params:scim:schemas:core:2.0:User" SchemaGroup = "urn:ietf:params:scim:schemas:core:2.0:Group" SchemaEnterprise = "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User" SchemaListResponse = "urn:ietf:params:scim:api:messages:2.0:ListResponse" SchemaPatchOp = "urn:ietf:params:scim:api:messages:2.0:PatchOp" SchemaError = "urn:ietf:params:scim:api:messages:2.0:Error" )
SCIM schema URNs.
const ( LoginPath = "/.pando/login" CompletePath = "/.pando/api/v1/auth/complete" )
Paths on every hostname Pando serves (R-172).
const ( OutcomeExisting = "existing" OutcomeLinked = "linked_by_email" OutcomeCreated = "created" OutcomeRefused = "refused" )
What a sign-in resolves to.
const FlowLifetime = 10 * time.Minute
FlowLifetime is how long a person has at the provider.
const RevocationWindow = 120 * time.Second
RevocationWindow is design 06 §3.1's number: the longest any revocation takes to take effect, once Pando knows about it.
Variables ¶
This section is empty.
Functions ¶
func ApplyPatch ¶
ApplyPatch applies operations to a resource held as a map. The caller validates the result as it would a PUT, so a patch can do nothing a replacement could not.
Members of a group are not handled here: they are relations, not attributes, and the caller takes them out first (MemberOps).
func Digest ¶
Digest is the SHA-256 of a high-entropy random value, hex-encoded. Used for the browser binding, the handoff code and the SCIM token, each of which is 256 random bits: nothing to brute-force, so the slow hash that protects passwords would only cost time on every request.
Types ¶
type Filter ¶
Filter is the one form of SCIM filter Pando answers: an attribute equal to a value. It is what every provisioning client sends to ask "does this person already exist", and anything richer would be a query language kept for nobody.
type Kind ¶
type Kind struct {
New func() api.IdentityAdapter
Info api.KindInfo
}
Kind is a compiled-in identity adapter kind (R-253): how to make one, and how to describe it to a form.
type Origin ¶
Origin is where a request came from, as scheme and host — the address a browser is sent back to when a flow finishes.
type PatchOp ¶
type PatchOp struct {
Op string `json:"op"`
Path string `json:"path"`
Value json.RawMessage `json:"value"`
}
PatchOp is one operation of a PATCH request.
type PatchRequest ¶
type PatchRequest struct {
Schemas []string `json:"schemas"`
Operations []PatchOp `json:"Operations"`
}
PatchRequest is a SCIM PATCH body.
type Problem ¶
type Problem struct {
Code string `json:"code"`
Message string `json:"message"`
Remedy string `json:"remedy,omitempty"`
}
Problem is an error as a page shows it.
type ProviderInput ¶
type ProviderInput struct {
Kind string `json:"kind"`
Name *string `json:"name"`
Config json.RawMessage `json:"config"`
Credentials map[string]string `json:"credentials"`
Enabled *bool `json:"enabled"`
JITProvisioning *bool `json:"jit_provisioning"`
LinkByEmail *bool `json:"link_by_email"`
SCIMIdentityAttribute *string `json:"scim_identity_attribute"`
}
ProviderInput is a create or update request. Credentials are write-only: stored encrypted, never returned (R-190). An empty credential value on update removes it; an absent one leaves it.
type ProviderView ¶
type ProviderView struct {
state.IdentityProvider
// Credentials names the secrets that are set, never their values.
Credentials []string `json:"credentials"`
// CallbackURL and EntityID are what the provider is told about Pando:
// the redirect URI for OIDC, the ACS URL and audience for SAML.
CallbackURL string `json:"callback_url,omitempty"`
EntityID string `json:"entity_id,omitempty"`
MetadataURL string `json:"metadata_url,omitempty"`
// SCIMBaseURL is where a SCIM client sends pushes, with the token.
SCIMBaseURL string `json:"scim_base_url,omitempty"`
// Revocation is R-047 made visible: the lifetime, the mode, and the
// window that results, per provider.
Revocation *Revocation `json:"revocation,omitempty"`
// Problem is why the provider cannot be used as configured, when it
// cannot. A provider with a problem stays listed so it can be fixed.
Problem string `json:"problem,omitempty"`
}
ProviderView is a provider as an administrator sees it: its settings, what to register with it, and how quickly it can revoke.
type PublicProvider ¶
type PublicProvider struct {
ID string `json:"id"`
Name string `json:"name"`
Kind string `json:"kind"`
}
PublicProvider is what the sign-in page shows: a name to press.
type Revocation ¶
type Revocation struct {
MaxLifetimeSeconds int64 `json:"max_lifetime_seconds"`
Mode string `json:"mode"`
// WindowSeconds is the longest access can outlive its removal at the
// provider: the session lifetime without push; with push, the two-minute
// window every revocation has anyway (design 06 §3.1).
WindowSeconds int64 `json:"window_seconds"`
}
Revocation is a provider's session behavior as it actually is on this install (R-047, R-050): the adapter's declaration, upgraded to push when a SCIM client can push, and the window that results.
type SCIMContext ¶
type SCIMContext struct {
Provider state.IdentityProvider
Base string
}
SCIMContext is who is pushing: the provider whose token was presented, and the address resources are reported at.
type SCIMError ¶
SCIMError is a SCIM error response (RFC 7644 §3.12).
type Service ¶
type Service struct {
Providers *state.IdentityProviders
Credentials *state.AdapterCredentials
Identities *state.Identities
Users *state.Users
Sessions *state.Sessions
Groups *state.Groups
Flows *state.SSOFlows
SCIMUsers *state.SCIMUsers
SCIMGroups *state.SCIMGroups
// Policy is host policy as in force, startup overlay included.
Policy interface {
Load(ctx context.Context) (policy.Document, error)
}
// Local is the local username-and-password adapter (R-041).
Local api.IdentityAdapter
// Kinds are the external kinds this build can run, by kind string.
Kinds map[string]Kind
// ExternalURL is PANDO_SERVER_EXTERNAL_URL. When set it is the only
// address callbacks go to; when not, the address a request arrived at.
ExternalURL *url.URL
// Audit records an event. The HTTP layer supplies one that adds the
// request ID.
Audit func(ctx context.Context, e audit.Event)
Clock clock.Clock
// contains filtered or unexported fields
}
Service is external identity.
func (*Service) Adapter ¶
func (s *Service) Adapter(ctx context.Context, providerID string) (api.IdentityAdapter, state.IdentityProvider, error)
Adapter returns a provider's adapter, configured from its stored settings and decrypted credentials.
Built on first use and rebuilt when the provider changes, so a provider an administrator adds or edits works on the next request — unlike other adapter categories, whose instances are fixed at startup (R-253). A sign-in page that needed a restart to show a new provider would be a restart in front of everyone who uses the install.
func (*Service) Callback ¶
func (s *Service) Callback(ctx context.Context, providerID string, params url.Values, o Origin) string
Callback handles the provider's response and returns where to send the browser next. It never returns an error for the browser to see raw: a failure is recorded on the flow and the browser is sent to a page that explains it.
func (*Service) CheckProvider ¶
CheckProvider asks the provider whether it answers: its discovery document or its metadata.
func (*Service) Complete ¶
func (s *Service) Complete(ctx context.Context, code, bind string, o Origin, userAgent, ip string) (Completed, error)
Complete spends a one-time code in the browser that started the flow.
func (*Service) CreateProvider ¶
func (s *Service) CreateProvider(ctx context.Context, p authz.Principal, in ProviderInput, o Origin) (ProviderView, error)
CreateProvider adds a provider. Off unless enabled is set: a provider that appears on the sign-in page before anyone has tested it is a button that fails for everyone who presses it.
func (*Service) DeleteProvider ¶
DeleteProvider removes a provider nobody has signed in through.
func (*Service) DisableSCIM ¶
DisableSCIM removes a provider's SCIM token. Accounts and groups it pushed stay; sign-in claims own group membership again from the next sign-in.
func (*Service) Endpoints ¶
Endpoints are a provider's callback URL and entity ID. What an administrator registers with the provider, so they are stable — computed from the external URL and the provider's ID, never from anything a person types.
func (*Service) FailureMessage ¶
FailureMessage is what the sign-in page shows for a failed flow. Only a sign-in's failure is shown, never a test's, and never anything but the message and remedy recorded when it failed.
func (*Service) IdentitiesFor ¶
Identities lists the identities that reach an account.
func (*Service) LinkGroup ¶
func (s *Service) LinkGroup(ctx context.Context, p authz.Principal, groupID, syncedGroupID string) error
LinkGroup makes everyone in a provider's group count as a member of a Pando-made group, live (R-079).
func (*Service) LinkIdentity ¶
func (s *Service) LinkIdentity(ctx context.Context, p authz.Principal, userID, providerID, externalID string, replace bool) (string, error)
LinkIdentity attaches a provider's identity to an account. When it already reaches another account, it moves only with replace set, and that account becomes a suspended alias of this one — never deleted (design 02 §2.1).
func (*Service) ListProviders ¶
ListProviders returns every provider with its endpoints and revocation.
func (*Service) Options ¶
func (s *Service) Options(ctx context.Context) (SignInOptions, error)
Options answers the sign-in page.
func (*Service) PasswordSignInAllowed ¶
PasswordSignInAllowed is the gate on POST /sessions.
func (*Service) RotateSCIMToken ¶
func (s *Service) RotateSCIMToken(ctx context.Context, p authz.Principal, providerID string) (secret.Value, error)
RotateSCIMToken turns SCIM on for a provider, or replaces its token. The token is returned once and kept only as a digest: whoever holds it can create, suspend and regroup that provider's accounts.
func (*Service) SCIMAuthenticate ¶
func (s *Service) SCIMAuthenticate(ctx context.Context, bearer string, o Origin) (SCIMContext, error)
SCIMAuthenticate resolves a bearer token to its provider.
func (*Service) SCIMBaseURL ¶
SCIMBaseURL is what an administrator gives a SCIM client.
func (*Service) SCIMCreateGroup ¶
func (s *Service) SCIMCreateGroup(ctx context.Context, c SCIMContext, body []byte) (map[string]any, error)
SCIMCreateGroup answers POST /Groups. The group has no access until an administrator gives it some, or links it to a Pando group (R-078).
func (*Service) SCIMCreateUser ¶
func (s *Service) SCIMCreateUser(ctx context.Context, c SCIMContext, body []byte) (map[string]any, error)
SCIMCreateUser answers POST /Users.
An account the provider's sign-in already made (just-in-time) is adopted rather than refused: the client is telling Pando about someone it already knows, and a conflict would leave that person unmanaged.
func (*Service) SCIMDeleteGroup ¶
SCIMDeleteGroup answers DELETE /Groups/{id}. The group goes, and every grant made to it with it — as when an administrator deletes one — unless that would leave nobody who can manage accounts (R-088).
func (*Service) SCIMDeleteUser ¶
SCIMDeleteUser answers DELETE /Users/{id}: the account is suspended, not deleted (R-049), and the client forgets it.
func (*Service) SCIMGetGroup ¶
func (s *Service) SCIMGetGroup(ctx context.Context, c SCIMContext, groupID string, excludeMembers bool) (map[string]any, error)
SCIMGetGroup answers GET /Groups/{id}.
func (*Service) SCIMGetUser ¶
func (s *Service) SCIMGetUser(ctx context.Context, c SCIMContext, userID string) (map[string]any, error)
SCIMGetUser answers GET /Users/{id}.
func (*Service) SCIMListGroups ¶
func (s *Service) SCIMListGroups(ctx context.Context, c SCIMContext, filter string, startIndex, count int, excludeMembers bool) (map[string]any, error)
SCIMListGroups answers GET /Groups.
func (*Service) SCIMListUsers ¶
func (s *Service) SCIMListUsers(ctx context.Context, c SCIMContext, filter string, startIndex, count int) (map[string]any, error)
SCIMListUsers answers GET /Users.
func (*Service) SCIMPatchGroup ¶
func (s *Service) SCIMPatchGroup(ctx context.Context, c SCIMContext, groupID string, body []byte) error
SCIMPatchGroup answers PATCH /Groups/{id}: membership changes, a rename. Answers 204, as Entra and Okta both accept, rather than recomputing a membership list the client did not ask to see.
func (*Service) SCIMPatchUser ¶
func (s *Service) SCIMPatchUser(ctx context.Context, c SCIMContext, userID string, body []byte) (map[string]any, error)
SCIMPatchUser answers PATCH /Users/{id}.
func (*Service) SCIMReplaceGroup ¶
func (s *Service) SCIMReplaceGroup(ctx context.Context, c SCIMContext, groupID string, body []byte) (map[string]any, error)
SCIMReplaceGroup answers PUT /Groups/{id}.
func (*Service) SCIMReplaceUser ¶
func (s *Service) SCIMReplaceUser(ctx context.Context, c SCIMContext, userID string, body []byte) (map[string]any, error)
SCIMReplaceUser answers PUT /Users/{id}.
func (*Service) SCIMResourceTypes ¶
func (s *Service) SCIMResourceTypes(c SCIMContext) map[string]any
SCIMResourceTypes answers GET /ResourceTypes.
func (*Service) SCIMSchemas ¶
func (s *Service) SCIMSchemas(c SCIMContext) map[string]any
SCIMSchemas answers GET /Schemas with the attributes Pando reads. Others a client sends are kept and returned, but not described.
func (*Service) SCIMServiceProviderConfig ¶
func (s *Service) SCIMServiceProviderConfig(c SCIMContext) map[string]any
SCIMServiceProviderConfig answers GET /ServiceProviderConfig.
func (*Service) ServiceMetadata ¶
func (s *Service) ServiceMetadata(ctx context.Context, o Origin, providerID string) (*api.Metadata, error)
ServiceMetadata is Pando's metadata for a provider — SAML SP metadata. Public: a provider fetches it without credentials.
func (*Service) SessionLifetime ¶
SessionLifetime is how long a session made through a provider lasts.
func (*Service) TestResult ¶
func (s *Service) TestResult(ctx context.Context, p authz.Principal, providerID, flowID string) (TestReport, error)
TestResult returns a test sign-in's report, to the administrator who ran it.
func (*Service) UnlinkGroup ¶
func (s *Service) UnlinkGroup(ctx context.Context, p authz.Principal, groupID, syncedGroupID string) error
UnlinkGroup removes such a link. Its members lose what the Pando group gave them, within the revocation window (design 06 §3.1).
func (*Service) UnlinkIdentity ¶
func (s *Service) UnlinkIdentity(ctx context.Context, p authz.Principal, userID, providerID, externalID string) error
UnlinkIdentity removes a provider's identity from an account. The account stays; whoever signs in with that identity next is a stranger again.
func (*Service) UpdateProvider ¶
func (s *Service) UpdateProvider(ctx context.Context, p authz.Principal, providerID string, in ProviderInput, o Origin) (ProviderView, error)
UpdateProvider changes a provider. Settings replace the stored ones whole when given; credentials change field by field.
type SignInOptions ¶
type SignInOptions struct {
PasswordSignIn bool `json:"password_sign_in"`
Providers []PublicProvider `json:"providers"`
}
SignInOptions is how people can sign in here.
type SignInOutcome ¶
type SignInOutcome struct {
Kind string `json:"kind"`
UserID string `json:"user_id,omitempty"`
Username string `json:"username,omitempty"`
DisplayName string `json:"display_name,omitempty"`
Message string `json:"message,omitempty"`
Remedy string `json:"remedy,omitempty"`
Groups []string `json:"groups,omitempty"`
// GroupsFrom is where the account's groups at this provider come from:
// "sign_in" (the groups claim, on every sign-in) or "scim".
GroupsFrom string `json:"groups_from"`
// contains filtered or unexported fields
}
SignInOutcome is which account a sign-in reaches, and how.
type StartRequest ¶
type StartRequest struct {
ProviderID string
Origin Origin
Next string
// Test is an administrator's test sign-in: nobody is signed in, and the
// report says what would have happened.
Test bool
InitiatedBy authz.Principal
}
StartRequest begins a redirect sign-in.
type Started ¶
Started is where to send the browser, and the value of the cookie that binds the flow to it.
type SubjectView ¶
type SubjectView struct {
ExternalID string `json:"external_id"`
Username string `json:"username,omitempty"`
Email string `json:"email,omitempty"`
EmailVerified bool `json:"email_verified"`
DisplayName string `json:"display_name,omitempty"`
Groups []string `json:"groups"`
}
SubjectView is a Subject as an administrator's test sign-in shows it.
type TestReport ¶
type TestReport struct {
OK bool `json:"ok"`
Error *Problem `json:"error,omitempty"`
Subject *SubjectView `json:"subject,omitempty"`
Attributes map[string][]string `json:"attributes,omitempty"`
Outcome *SignInOutcome `json:"outcome,omitempty"`
}
TestReport is what a test sign-in found: what the provider sent, and what Pando would have done with it.