Help us shape the future of Go! Take the 2026 Go Developer Survey

scim

package
v0.1.0-alpha.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 23, 2026 License: MIT Imports: 28 Imported by: 0

Documentation

Overview

Package scim provides optional organization-bound SCIM 2.0 provisioning and deprovisioning for PostgreSQL and SQLite (RFC 7643 and RFC 7644). An organization's owner creates a directory and issues it a bearer token; the organization's identity provider then creates, updates, deactivates and deletes users and groups through the SCIM endpoint the host mounts. A directory provisions accounts only for addresses in domains its organization verified through DNS, binds each account to at most one directory, and never takes over an account unless its owner opted in. It depends on the organization and sso plugins. See docs/scim.md.

Index

Constants

View Source
const (
	DirectoryCreateOperation plugin.Operation = "scim.directory.create"
	DirectoryUpdateOperation plugin.Operation = "scim.directory.update"
	DirectoryDeleteOperation plugin.Operation = "scim.directory.delete"
	TokenIssueOperation      plugin.Operation = "scim.token.issue"
	TokenRevokeOperation     plugin.Operation = "scim.token.revoke"
	// UserCreateOperation records a new account the directory created.
	UserCreateOperation plugin.Operation = "scim.user.create"
	// UserBindOperation records an existing account the directory adopted.
	UserBindOperation       plugin.Operation = "scim.user.bind"
	UserUpdateOperation     plugin.Operation = "scim.user.update"
	UserDeactivateOperation plugin.Operation = "scim.user.deactivate"
	UserReactivateOperation plugin.Operation = "scim.user.reactivate"
	UserDeleteOperation     plugin.Operation = "scim.user.delete"
	GroupCreateOperation    plugin.Operation = "scim.group.create"
	GroupUpdateOperation    plugin.Operation = "scim.group.update"
	GroupDeleteOperation    plugin.Operation = "scim.group.delete"
	// AuthenticateOperation records a refused provisioning token.
	AuthenticateOperation plugin.Operation = "scim.authenticate"
)

Operations recorded in the audit trail. Management operations are attributed to the organization owner's session. Provisioning operations are attributed to the host with the directory's method, "scim.<directory id>", and the affected account as subject.

View Source
const (
	// ReasonDomain: the address is not in a domain the organization verified.
	ReasonDomain sigil.AuditReason = "domain"
	// ReasonConflict: the address, account or linked identity belongs to
	// someone the directory may not take over.
	ReasonConflict sigil.AuditReason = "conflict"
)

Refusal reasons on provisioning failures, beside sigil.ReasonForbidden (a deprovisioning request for an organization owner) and sigil.ReasonInvalidCredentials (an unknown, forged or revoked token).

View Source
const (
	MaxDirectories  = 5     // per organization
	MaxTokens       = 2     // live tokens per directory, for rotation
	MaxPage         = 100   // resources per list response
	MaxGroups       = 1000  // per directory
	MaxGroupMembers = 10000 // per group
	// MaxMemberPage bounds the groups of one list page that include members.
	MaxMemberPage = 10
	MaxOperations = 100 // per PATCH request
	MaxEmails     = 10  // per user

)

Limits.

View Source
const ID = "scim"

Variables

This section is empty.

Functions

func Contract

func Contract() plugin.Definition

Contract is the offline declaration for tooling: routes, schema names and migrations are independent of configuration. Its handlers fail closed.

func Method

func Method(directoryID string) sigil.AuditMethod

Method names a directory on provisioning events: "scim.<id>".

Types

type Config

type Config struct {
	Store storage.Store
	// BaseURL is the absolute HTTPS URL at which the host mounts Handler, for
	// example "https://auth.example.com/scim/v2". Resource locations are
	// derived from it and the handler serves only paths below it.
	BaseURL string
	// ManageAssurance raises the assurance directory and token management need
	// above sigil.Config.SensitiveAssurance; it never lowers it.
	ManageAssurance sigil.AssuranceLevel
}

Config resources belong to the Auth passed to Bind.

type Directory

type Directory struct {
	ID             string `json:"id"`
	OrganizationID string `json:"organizationId"`
	// ConnectionID, when set, names one of the organization's SSO connections:
	// every account the directory provisions is linked to it with the
	// LinkSubject attribute as subject, so it can sign in with no password.
	ConnectionID  string      `json:"connectionId"`
	LinkSubject   LinkSubject `json:"linkSubject"`
	AdoptExisting bool        `json:"adoptExisting"`
	Enabled       bool        `json:"enabled"`
	Revision      int         `json:"revision"`
	Tokens        []Token     `json:"tokens"`
	CreatedAt     time.Time   `json:"createdAt"`
	UpdatedAt     time.Time   `json:"updatedAt"`
}

Directory is an organization's provisioning client. Tokens lists live tokens without their secrets.

type DirectoryInput

type DirectoryInput struct {
	ConnectionID  string
	LinkSubject   LinkSubject
	AdoptExisting bool
	Enabled       bool
}

DirectoryInput is the configuration an owner submits. ConnectionID and LinkSubject are set together or not at all.

type Error

type Error struct {
	Status   int
	ScimType string
	Detail   string
}

Error is a SCIM error response (RFC 7644 section 3.12). Detail is fixed, library-controlled text; it never echoes request values.

func (*Error) Error

func (e *Error) Error() string

type Group

type Group struct {
	ID          string `json:"id"`
	DirectoryID string `json:"directoryId"`
	DisplayName string `json:"displayName"`
	ExternalID  string `json:"externalId"`
}

Group is a directory group as the host reads it for its own authorization. Groups grant nothing in Sigil: they do not change organization roles.

func MemberGroups

func MemberGroups(tx storage.Tx, organizationID, userID string) ([]Group, error)

MemberGroups lists, inside a trusted transaction, the groups of the directory of organizationID that holds userID's account, by group ID. An account no directory of the organization holds has none. It is for host authorization decisions made in the same transaction as the work they guard; groups follow the directory, not the account's membership.

type IssuedToken

type IssuedToken struct {
	Token  Token
	Secret sigil.Secret
}

IssuedToken carries the one-time secret, which sigil.Secret redacts from logs and fmt output.

type LinkSubject

type LinkSubject string

LinkSubject names the SCIM attribute whose value is the subject a directory links for its SSO connection.

const (
	// LinkUserName links the userName, which is the SAML NameID of Microsoft
	// Entra ID (UPN), Okta (username) and Google Workspace (primary email).
	LinkUserName LinkSubject = "userName"
	// LinkExternalID links the externalId, for an IdP that maps its immutable
	// user identifier to both externalId and the SSO subject.
	LinkExternalID LinkSubject = "externalId"
)

type Plugin

type Plugin struct {
	// contains filtered or unexported fields
}

Plugin binds to exactly one Auth. Methods are safe for concurrent use.

func New

func New(c Config) (*Plugin, error)

New validates and copies configuration.

func (*Plugin) Bind

func (p *Plugin) Bind(a *sigil.Auth) error

func (*Plugin) CreateDirectory

func (p *Plugin) CreateDirectory(ctx context.Context, organizationID string, in DirectoryInput) (Directory, error)

CreateDirectory adds a directory to the organization. The caller must be its live owner, recently authenticated at Config.ManageAssurance. A linked SSO connection must belong to the same organization. The directory has no token until IssueToken.

func (*Plugin) Definition

func (p *Plugin) Definition() plugin.Definition

func (*Plugin) DeleteDirectory

func (p *Plugin) DeleteDirectory(ctx context.Context, organizationID, id string) error

DeleteDirectory removes a directory with its tokens, user records and groups. Accounts it provisioned or adopted are not changed: they remain ordinary accounts with their memberships and SSO links, and no longer follow the directory.

func (*Plugin) Directories

func (p *Plugin) Directories(ctx context.Context, organizationID string) ([]Directory, error)

Directories lists the organization's directories, oldest first, for its live owner. Token secrets are never included.

func (*Plugin) Handler

func (p *Plugin) Handler() http.Handler

Handler serves the SCIM 2.0 protocol at Config.BaseURL for identity providers. Every request needs one "Authorization: Bearer" provisioning token; cookies, query strings and bodies never carry credentials, so the endpoint needs no CSRF protection and must not be given CORS headers. The token selects the directory, and every resource is scoped to it. Mount it outside Auth.Handler, for example:

mux.Handle("/scim/v2/", scim.Handler())

The host owns TLS, request-rate and client-address policy for the route.

func (*Plugin) IssueToken

func (p *Plugin) IssueToken(ctx context.Context, organizationID, directoryID string) (IssuedToken, error)

IssueToken creates a provisioning token for the directory and returns its secret once. A directory holds at most MaxTokens live tokens: to rotate, issue the next token, configure it at the identity provider, then revoke the old one. Tokens do not expire.

func (*Plugin) RevokeToken

func (p *Plugin) RevokeToken(ctx context.Context, organizationID, directoryID, tokenID string) error

RevokeToken deletes one of the directory's tokens. Every request made with it afterwards, including one in flight that has not yet written, is refused. Revoking an unknown or already revoked token is forbidden.

func (*Plugin) UpdateDirectory

func (p *Plugin) UpdateDirectory(ctx context.Context, organizationID, id string, revision int, in DirectoryInput) (Directory, error)

UpdateDirectory replaces a directory's configuration at the expected revision, under the same authorization as CreateDirectory. Disabling refuses every token at once. A changed connection or link subject applies to each user record the next time the directory writes it; links created earlier remain until then.

type Token

type Token struct {
	ID         string     `json:"id"`
	CreatedAt  time.Time  `json:"createdAt"`
	LastUsedAt *time.Time `json:"lastUsedAt"`
}

Token describes a live provisioning token. The secret is returned once.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL