invite

package
v0.14.0 Latest Latest
Warning

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

Go to latest
Published: Oct 11, 2026 License: MIT Imports: 27 Imported by: 0

Documentation

Overview

Package invite provides organization invitation management.

Index

Constants

View Source
const DefaultInviteExpiry = 7 * 24 * time.Hour // 7 days

Default invite expiration time.

Variables

View Source
var ErrAppRoleRequired = errors.New("invite: choose an application role for at least one entitled application")

ErrAppRoleRequired means an admin or member invitation chose no application role although the organization is entitled to applications (ADR-007: nobody gets into an application by being a member alone, and an invitation must open at least one).

View Source
var ErrResourceInvite = errors.New("invite: invalid resource target")

ErrResourceInvite means an invitation's resource target is incomplete, is not for a collaborator, or cannot be honored by the service.

Functions

This section is empty.

Types

type API

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

API provides HTTP handlers for Invite management.

func NewAPI

func NewAPI(service Service, orgSvc organization.Service, config APIConfig) *API

NewAPI creates a new Invite API.

func (*API) Router

func (a *API) Router() chi.Router

Router returns the Chi router.

func (*API) ServeHTTP

func (a *API) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements http.Handler.

type APIConfig

type APIConfig struct {
	// BasePath is the base path for all endpoints (default: "/api/v1").
	BasePath string
	// Logger is the logger to use (default: slog.Default()).
	Logger *slog.Logger
	// Authorizer checks the caller's organization permissions; managing
	// an organization's invites needs authz.PermOrgInviteMember. Until it
	// is set, those endpoints answer 501.
	Authorizer authz.Authorizer
	// Caller identifies the authenticated principal (default:
	// requestprincipal.PrincipalUUID, set by requestprincipal.Middleware).
	Caller func(ctx context.Context) (uuid.UUID, error)
	// PrincipalEmail returns the caller's email, which an invite must be
	// addressed to (default: the service's PrincipalEmail, when it has
	// one, as DefaultService does).
	PrincipalEmail PrincipalEmailFunc
}

APIConfig configures the Invite API.

func DefaultAPIConfig

func DefaultAPIConfig() APIConfig

DefaultAPIConfig returns the default API configuration.

type AcceptInviteInput

type AcceptInviteInput struct {
	Token       string
	PrincipalID uuid.UUID // The principal accepting the invite
}

AcceptInviteInput contains input for accepting an invite.

type AcceptInviteRequest

type AcceptInviteRequest struct {
	Token string `path:"token"`
	Body  struct {
		PrincipalID string `` /* 169-byte string literal not displayed */
	}
}

AcceptInviteRequest is the request for accepting an invite.

type AcceptInviteResponse

type AcceptInviteResponse struct {
	Body *InviteResponse
}

AcceptInviteResponse is the response for accepting an invite.

type CreateInviteInput

type CreateInviteInput struct {
	OrganizationID     uuid.UUID
	InviterPrincipalID uuid.UUID
	Email              string
	Role               string
	// AppRoles are the application roles to grant on acceptance:
	// application identifier to role. None is preselected; an inviter
	// chooses each. With an approle service configured (WithAppRoles),
	// an admin or member invitation to an organization entitled to any
	// application must name at least one (ErrAppRoleRequired).
	AppRoles map[string]string
	// ResourceType, ResourceID and ResourceRole target the invite at one
	// resource (ADR-008); set all three, or none. They require Role to be
	// "collaborator" and the service to be built with WithResourceGrants.
	// A personal organization accepts only resource-targeted invites.
	ResourceType string
	ResourceID   string
	ResourceRole string
	Message      *string
	ExpiresIn    time.Duration // How long until expiry (default: 7 days)
}

CreateInviteInput contains input for creating an invite.

type CreateInviteRequest

type CreateInviteRequest struct {
	Slug string `path:"slug"`
	Body struct {
		Email              string            `json:"email" required:"true" format:"email"`
		Role               string            `json:"role" required:"true" enum:"owner,admin,member,collaborator"`
		AppRoles           map[string]string `` /* 126-byte string literal not displayed */
		ResourceType       string            `` /* 179-byte string literal not displayed */
		ResourceID         string            `json:"resource_id,omitempty" doc:"The application's identifier of the resource"`
		ResourceRole       string            `json:"resource_role,omitempty" enum:"viewer,contributor,maintainer" doc:"The resource role granted on acceptance"`
		Message            *string           `json:"message,omitempty"`
		InviterPrincipalID string            `` /* 165-byte string literal not displayed */
		ExpiresInHours     int               `json:"expires_in_hours,omitempty" minimum:"1" maximum:"720"`
	}
}

CreateInviteRequest is the request for creating an invite.

type CreateInviteResponse

type CreateInviteResponse struct {
	Body *InviteResultResponse
}

CreateInviteResponse is the response for creating an invite.

type DeclineInviteRequest

type DeclineInviteRequest struct {
	Token string `path:"token"`
}

DeclineInviteRequest is the request for declining an invite.

type DeclineInviteResponse

type DeclineInviteResponse struct {
	Body *InviteResponse
}

DeclineInviteResponse is the response for declining an invite.

type DefaultService

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

DefaultService implements the Service interface.

func (*DefaultService) Accept

func (s *DefaultService) Accept(ctx context.Context, input AcceptInviteInput) (*Invite, error)

Accept accepts an invite and creates the membership.

func (*DefaultService) Create

Create creates a new invite.

func (*DefaultService) Decline

func (s *DefaultService) Decline(ctx context.Context, token string) (*Invite, error)

Decline declines an invite.

func (*DefaultService) ExpireOld

func (s *DefaultService) ExpireOld(ctx context.Context) (int, error)

ExpireOld expires invites that have passed their expiration time.

func (*DefaultService) GetByID

func (s *DefaultService) GetByID(ctx context.Context, id uuid.UUID) (*Invite, error)

GetByID retrieves an invite by ID.

func (*DefaultService) GetByToken

func (s *DefaultService) GetByToken(ctx context.Context, token string) (*Invite, error)

GetByToken retrieves an invite by its token.

func (*DefaultService) HasPendingInvite

func (s *DefaultService) HasPendingInvite(ctx context.Context, organizationID uuid.UUID, email string) (bool, error)

HasPendingInvite checks if there's already a pending invite for this email/org.

func (*DefaultService) List

func (s *DefaultService) List(ctx context.Context, input ListInvitesInput) ([]*Invite, error)

List lists invites with optional filters.

func (*DefaultService) ListPendingForEmail

func (s *DefaultService) ListPendingForEmail(ctx context.Context, email string) ([]*Invite, error)

ListPendingForEmail lists pending invites for an email address.

func (*DefaultService) PrincipalEmail added in v0.14.0

func (s *DefaultService) PrincipalEmail(ctx context.Context, principalID uuid.UUID) (string, error)

PrincipalEmail returns a principal's email from the identity database, or "" when it has none. It is the API's default PrincipalEmailFunc.

func (*DefaultService) Resend

func (s *DefaultService) Resend(ctx context.Context, id uuid.UUID) (*InviteResult, error)

Resend resends an invite with a new token.

func (*DefaultService) Revoke

func (s *DefaultService) Revoke(ctx context.Context, id uuid.UUID) error

Revoke revokes an invite.

type GetInviteByTokenRequest

type GetInviteByTokenRequest struct {
	Token string `path:"token"`
}

GetInviteByTokenRequest is the request for getting an invite by token.

type GetInviteByTokenResponse

type GetInviteByTokenResponse struct {
	Body *InviteResponse
}

GetInviteByTokenResponse is the response for getting an invite.

type Invite

type Invite struct {
	ID                 uuid.UUID
	OrganizationID     uuid.UUID
	InviterPrincipalID uuid.UUID
	Email              string
	Role               string
	// AppRoles are the application roles granted when the invite is
	// accepted: application identifier to role. Empty means none.
	AppRoles map[string]string
	// ResourceType, ResourceID and ResourceRole, all set or all empty,
	// target the invite at one resource (ADR-008): accepting it makes the
	// invitee a collaborator of the organization and grants ResourceRole on
	// the resource in one transaction. Role is then "collaborator".
	ResourceType          string
	ResourceID            string
	ResourceRole          string
	Status                Status
	Message               *string
	ExpiresAt             time.Time
	AcceptedAt            *time.Time
	AcceptedByPrincipalID *uuid.UUID
	ResendCount           int
	LastSentAt            time.Time
	CreatedAt             time.Time
	UpdatedAt             time.Time
}

Invite represents an invitation to join an organization.

func (*Invite) CanAccept

func (i *Invite) CanAccept() bool

CanAccept returns true if the invite can be accepted.

func (*Invite) IsExpired

func (i *Invite) IsExpired() bool

IsExpired returns true if the invite has expired.

func (*Invite) IsPending

func (i *Invite) IsPending() bool

IsPending returns true if the invite is still pending.

type InviteResponse

type InviteResponse struct {
	ID                    string            `json:"id"`
	OrganizationID        string            `json:"organization_id"`
	InviterPrincipalID    string            `json:"inviter_principal_id"`
	Email                 string            `json:"email"`
	Role                  string            `json:"role"`
	AppRoles              map[string]string `json:"app_roles,omitempty"`
	ResourceType          string            `json:"resource_type,omitempty"`
	ResourceID            string            `json:"resource_id,omitempty"`
	ResourceRole          string            `json:"resource_role,omitempty"`
	Status                string            `json:"status"`
	Message               *string           `json:"message,omitempty"`
	ExpiresAt             time.Time         `json:"expires_at"`
	AcceptedAt            *time.Time        `json:"accepted_at,omitempty"`
	AcceptedByPrincipalID *string           `json:"accepted_by_principal_id,omitempty"`
	ResendCount           int               `json:"resend_count"`
	LastSentAt            time.Time         `json:"last_sent_at"`
	CreatedAt             time.Time         `json:"created_at"`
	UpdatedAt             time.Time         `json:"updated_at"`
}

InviteResponse is the API representation of an invite.

type InviteResult

type InviteResult struct {
	Invite *Invite
	// InviteURL is the full URL to accept the invite
	InviteURL string
}

InviteResult contains the result of invite operations.

type InviteResultResponse

type InviteResultResponse struct {
	Invite    *InviteResponse `json:"invite"`
	InviteURL string          `json:"invite_url"`
}

InviteResultResponse includes the invite URL.

type ListInvitesInput

type ListInvitesInput struct {
	OrganizationID *uuid.UUID
	Email          *string
	Status         *Status
	Limit          int
	Offset         int
}

ListInvitesInput contains filters for listing invites.

type ListInvitesRequest

type ListInvitesRequest struct {
	Slug string `path:"slug"`
	// Huma does not support pointer parameters; empty lists every status.
	Status string `query:"status" enum:"pending,accepted,declined,expired,revoked"`
	Limit  int    `query:"limit" default:"20" minimum:"1" maximum:"100"`
	Offset int    `query:"offset" default:"0" minimum:"0"`
}

ListInvitesRequest is the request for listing invites.

type ListInvitesResponse

type ListInvitesResponse struct {
	Body struct {
		Invites []*InviteResponse `json:"invites"`
	}
}

ListInvitesResponse is the response for listing invites.

type ListPendingInvitesForEmailRequest

type ListPendingInvitesForEmailRequest struct {
	// Email is deprecated: the invites listed are the caller's. When
	// present it must be the caller's email.
	Email string `` /* 130-byte string literal not displayed */
}

ListPendingInvitesForEmailRequest is the request for listing pending invites by email.

type ListPendingInvitesForEmailResponse

type ListPendingInvitesForEmailResponse struct {
	Body struct {
		Invites []*InviteResponse `json:"invites"`
	}
}

ListPendingInvitesForEmailResponse is the response for listing pending invites.

type PrincipalEmailFunc added in v0.14.0

type PrincipalEmailFunc func(ctx context.Context, principalID uuid.UUID) (string, error)

PrincipalEmailFunc returns a principal's email, or "" when it has none (a managed principal).

type ResendInviteRequest

type ResendInviteRequest struct {
	Slug     string `path:"slug"`
	InviteID string `path:"invite_id" format:"uuid"`
}

ResendInviteRequest is the request for resending an invite.

type ResendInviteResponse

type ResendInviteResponse struct {
	Body *InviteResultResponse
}

ResendInviteResponse is the response for resending an invite.

type RevokeInviteRequest

type RevokeInviteRequest struct {
	Slug     string `path:"slug"`
	InviteID string `path:"invite_id" format:"uuid"`
}

RevokeInviteRequest is the request for revoking an invite.

type Service

type Service interface {
	// Create creates a new invite.
	Create(ctx context.Context, input CreateInviteInput) (*InviteResult, error)

	// GetByToken retrieves an invite by its token.
	GetByToken(ctx context.Context, token string) (*Invite, error)

	// GetByID retrieves an invite by ID.
	GetByID(ctx context.Context, id uuid.UUID) (*Invite, error)

	// Accept accepts an invite and creates the membership.
	Accept(ctx context.Context, input AcceptInviteInput) (*Invite, error)

	// Decline declines an invite.
	Decline(ctx context.Context, token string) (*Invite, error)

	// Revoke revokes an invite.
	Revoke(ctx context.Context, id uuid.UUID) error

	// Resend resends an invite with a new token.
	Resend(ctx context.Context, id uuid.UUID) (*InviteResult, error)

	// List lists invites with optional filters.
	List(ctx context.Context, input ListInvitesInput) ([]*Invite, error)

	// ListPendingForEmail lists pending invites for an email address.
	ListPendingForEmail(ctx context.Context, email string) ([]*Invite, error)

	// ExpireOld expires invites that have passed their expiration time.
	ExpireOld(ctx context.Context) (int, error)

	// HasPendingInvite checks if there's already a pending invite for this email/org.
	HasPendingInvite(ctx context.Context, organizationID uuid.UUID, email string) (bool, error)
}

Service defines the invite service interface.

func NewService

func NewService(client *ent.Client, baseURL string, opts ...ServiceOption) Service

NewService creates a new invite service.

type ServiceOption added in v0.14.0

type ServiceOption func(*DefaultService)

ServiceOption configures a DefaultService.

func WithAppRoles added in v0.14.0

func WithAppRoles(svc *approle.Service) ServiceOption

WithAppRoles lets invitations carry application roles: Create validates them and Accept grants them in the same transaction as the membership. Without it, an invitation's AppRoles are rejected.

func WithAuthzSyncer added in v0.14.0

func WithAuthzSyncer(syncer authz.RelationshipSyncer) ServiceOption

WithAuthzSyncer sets the authorization syncer used to register accepted memberships in the authorization graph.

func WithLogger added in v0.14.0

func WithLogger(logger *slog.Logger) ServiceOption

WithLogger sets the logger for the service.

func WithResourceGrants added in v0.14.0

func WithResourceGrants(svc *resourcegrant.Service) ServiceOption

WithResourceGrants lets invitations target a resource (ADR-008): Create validates the target against the grant service's rules and Accept creates the collaborator membership and the grant in one transaction, through the service's GrantInTx. Without it, a resource target is refused. Memberships are written to the authorization graph through this service's own syncer, grants through the grant service's.

func WithSyncMode added in v0.14.0

func WithSyncMode(mode authz.SyncMode) ServiceOption

WithSyncMode sets the sync mode (strict or eventual).

type Status

type Status string

Status represents the status of an invite.

const (
	StatusPending  Status = "pending"
	StatusAccepted Status = "accepted"
	StatusDeclined Status = "declined"
	StatusExpired  Status = "expired"
	StatusRevoked  Status = "revoked"
)

Jump to

Keyboard shortcuts

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