appleid

package
v0.40.13 Latest Latest
Warning

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

Go to latest
Published: Oct 30, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package appleid keeps the link between a Rivolt account and its Sign in with Apple identity (App Store 4.8; architect's design, DA-approved Oct 24): the identity's stable `sub`, which Apple's server-to-server notifications use to name the user, and the refresh token, kept only so deleting the account can revoke it.

The `sub` is in apple_identities; the refresh token is sealed in user_secrets ("apple.refresh_token") like the Rivian session. Neither is ever logged.

Index

Constants

View Source
const (
	EventConsentRevoked = "consent-revoked"
	EventAccountDelete  = "account-delete"
	EventEmailDisabled  = "email-disabled"
	EventEmailEnabled   = "email-enabled"
)

Event types in the `events` claim.

View Source
const MaxNotificationBody = 16 << 10

MaxNotificationBody caps the request body (a real notification is about 1 KB).

View Source
const NotificationFreshness = time.Hour

NotificationFreshness is how old a notification's `iat` may be.

View Source
const SecretName = "apple.refresh_token"

SecretName is the user_secrets entry holding Apple's refresh token for a user.

Variables

View Source
var ErrSubInUse = errors.New("appleid: this Apple identity belongs to another account")

ErrSubInUse is returned when an Apple identity is already linked to a different account.

Functions

This section is empty.

Types

type Claims

type Claims struct {
	IssuedAt time.Time
	Event    Event
}

Claims are the fields of a verified notification the handler uses.

type Event

type Event struct {
	Type string `json:"type"`
	Sub  string `json:"sub"`
}

Event is the decoded `events` claim of a notification.

type Mailer

type Mailer interface {
	Send(ctx context.Context, m email.Message) error
}

Mailer sends the account-delete notice (email.Client).

type NotificationStore

type NotificationStore interface {
	UserBySub(ctx context.Context, sub string) (uuid.UUID, bool, error)
	DropToken(ctx context.Context, userID uuid.UUID) error
	MarkSeen(ctx context.Context, hash []byte) (bool, error)
	Unsee(ctx context.Context, hash []byte) error
	SetEmailUndeliverable(ctx context.Context, userID uuid.UUID, v bool) error
	DeliverableEmail(ctx context.Context, userID uuid.UUID) (string, error)
}

NotificationStore is the part of Store the endpoint uses.

type Notifier

type Notifier struct {
	Verify   Verifier
	Store    NotificationStore
	Sessions SessionRevoker
	Mail     Mailer // nil: no email is sent
	Logger   *slog.Logger
	Now      func() time.Time
}

Notifier serves the notifications endpoint. Wrap it with a body cap and a rate limit where it is mounted.

func (*Notifier) ServeHTTP

func (n *Notifier) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP handles POST /api/auth/apple/notifications.

type SessionRevoker

type SessionRevoker interface {
	RevokeAll(ctx context.Context, userID uuid.UUID) (int64, error)
}

SessionRevoker signs a user out everywhere (sessions.Store).

type Store

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

Store links accounts to Apple identities. It works on the system pool (sign-in and Apple's notifications run before or outside a user's request scope).

func New

func New(db *sql.DB, s *secrets.Store) *Store

New builds a Store.

func (*Store) DeliverableEmail

func (s *Store) DeliverableEmail(ctx context.Context, userID uuid.UUID) (string, error)

DeliverableEmail is the account's verified address, or "" if it has none or it is marked undeliverable.

func (*Store) DropToken

func (s *Store) DropToken(ctx context.Context, userID uuid.UUID) error

DropToken forgets the account's Apple refresh token (consent revoked, or revoked at deletion). The link stays.

func (s *Store) Link(ctx context.Context, userID uuid.UUID, sub, refreshToken string) error

Link records that userID signs in with the Apple identity sub, and keeps refreshToken (when Apple issued one) for revocation. Linking the same account again is fine (the token is replaced); a sub already linked to another account is ErrSubInUse, and an account already linked to another sub keeps its first link.

func (*Store) MarkSeen

func (s *Store) MarkSeen(ctx context.Context, hash []byte) (bool, error)

MarkSeen records a notification hash and reports whether it was new.

func (*Store) PurgeSeen

func (s *Store) PurgeSeen(ctx context.Context) (int64, error)

PurgeSeen deletes replay-guard rows older than two days (retention job).

func (*Store) RefreshToken

func (s *Store) RefreshToken(ctx context.Context, userID uuid.UUID) (string, error)

RefreshToken returns the account's kept Apple refresh token, or "" when there is none.

func (*Store) SetEmailUndeliverable

func (s *Store) SetEmailUndeliverable(ctx context.Context, userID uuid.UUID, v bool) error

SetEmailUndeliverable marks (or clears) the account's address as not reachable, for non-essential mail.

func (*Store) Unsee

func (s *Store) Unsee(ctx context.Context, hash []byte) error

Unsee forgets a notification hash (the action failed; Apple will retry).

func (*Store) UserBySub

func (s *Store) UserBySub(ctx context.Context, sub string) (uuid.UUID, bool, error)

UserBySub returns the account an Apple identity is linked to, or false.

func (*Store) UsernameBySub

func (s *Store) UsernameBySub(ctx context.Context, sub string) (string, bool, error)

UsernameBySub returns the account key (users.username) of the account an Apple identity is linked to, or false.

type Verifier

type Verifier interface {
	Verify(ctx context.Context, rawJWT string) (Claims, error)
}

Verifier checks a notification JWT's signature, issuer and audience and returns its claims.

func NewVerifier

func NewVerifier(ctx context.Context, issuer, clientID string) Verifier

NewVerifier verifies against keys at issuer+"/auth/keys" (cached, refetched on an unknown key id) for the Services ID clientID. Apple's notifications carry an `exp` that we don't rely on: freshness is the `iat` rule.

Jump to

Keyboard shortcuts

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