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
- Variables
- func Hash(raw string) []byte
- type Claims
- type Event
- type LinkIntent
- type Mailer
- type NotificationStore
- type Notifier
- type SessionRevoker
- type Store
- func (s *Store) ConsumeLinkIntent(ctx context.Context, stateHash []byte, now time.Time) (LinkIntent, bool, error)
- func (s *Store) CreateLinkIntent(ctx context.Context, userID uuid.UUID, stateHash, sessionHash []byte, ...) error
- func (s *Store) DeliverableEmail(ctx context.Context, userID uuid.UUID) (string, error)
- func (s *Store) DropSessionLinkIntents(ctx context.Context, sessionHash []byte) error
- func (s *Store) DropToken(ctx context.Context, userID uuid.UUID) error
- func (s *Store) Link(ctx context.Context, userID uuid.UUID, sub, refreshToken string) error
- func (s *Store) Linked(ctx context.Context, userID uuid.UUID) (bool, error)
- func (s *Store) MarkSeen(ctx context.Context, hash []byte) (bool, error)
- func (s *Store) PurgeLinkIntents(ctx context.Context) (int64, error)
- func (s *Store) PurgeSeen(ctx context.Context) (int64, error)
- func (s *Store) RefreshToken(ctx context.Context, userID uuid.UUID) (string, error)
- func (s *Store) SetEmailUndeliverable(ctx context.Context, userID uuid.UUID, v bool) error
- func (s *Store) Unlink(ctx context.Context, userID uuid.UUID) error
- func (s *Store) Unsee(ctx context.Context, hash []byte) error
- func (s *Store) UserBySub(ctx context.Context, sub string) (uuid.UUID, bool, error)
- func (s *Store) UsernameBySub(ctx context.Context, sub string) (string, bool, error)
- type Verifier
Constants ¶
const ( EventConsentRevoked = "consent-revoked" EventAccountDelete = "account-delete" EventEmailDisabled = "email-disabled" EventEmailEnabled = "email-enabled" )
Event types in the `events` claim.
const LinkIntentTTL = 10 * time.Minute
LinkIntentTTL is how long a started link stays open: the time to get through Apple's sign-in page.
const MaxNotificationBody = 16 << 10
MaxNotificationBody caps the request body (a real notification is about 1 KB).
const NotificationFreshness = time.Hour
NotificationFreshness is how old a notification's `iat` may be.
const SecretName = "apple.refresh_token"
SecretName is the user_secrets entry holding Apple's refresh token for a user.
Variables ¶
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 ¶
Types ¶
type LinkIntent ¶ added in v0.40.19
LinkIntent is a consumed link request: who started it and from which session.
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.
type SessionRevoker ¶
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 (*Store) ConsumeLinkIntent ¶ added in v0.40.19
func (s *Store) ConsumeLinkIntent(ctx context.Context, stateHash []byte, now time.Time) (LinkIntent, bool, error)
ConsumeLinkIntent takes the intent for stateHash, once: the row is deleted whether or not it is still good. ok is false when there is none, or it has expired.
func (*Store) CreateLinkIntent ¶ added in v0.40.19
func (s *Store) CreateLinkIntent(ctx context.Context, userID uuid.UUID, stateHash, sessionHash []byte, now time.Time) error
CreateLinkIntent records that userID, signed in with the session whose hash is sessionHash, started linking an Apple identity with the OAuth state whose hash is stateHash. A user has at most one open intent: starting again replaces the earlier one.
func (*Store) DeliverableEmail ¶
DeliverableEmail is the account's verified address, or "" if it has none or it is marked undeliverable.
func (*Store) DropSessionLinkIntents ¶ added in v0.40.19
DropSessionLinkIntents drops every open intent started from the session whose hash is sessionHash (sign-out).
func (*Store) DropToken ¶
DropToken forgets the account's Apple refresh token (consent revoked, or revoked at deletion). The link stays.
func (*Store) Link ¶
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) Linked ¶ added in v0.40.19
Linked reports whether the account has an Apple identity linked.
func (*Store) PurgeLinkIntents ¶ added in v0.40.19
PurgeLinkIntents is the retention sweep: intents created more than a day ago, spent or not.
func (*Store) RefreshToken ¶
RefreshToken returns the account's kept Apple refresh token, or "" when there is none.
func (*Store) SetEmailUndeliverable ¶
SetEmailUndeliverable marks the account's address as not reachable, for non-essential mail, or clears the mark. Marking only applies to an account whose address IS an Apple relay address: Apple's "forwarding off" event is about that relay, so an account with its own address (a linked account, or one that joined by sharing its email) is left alone. Clearing is always allowed.
func (*Store) Unlink ¶ added in v0.40.19
Unlink removes the account's Apple identity and forgets the kept refresh token. The caller has revoked the token at Apple, or queued that, first: after this the token is gone from here. Unlinking an account that is not linked is not an error.
type Verifier ¶
Verifier checks a notification JWT's signature, issuer and audience and returns its claims.
func NewVerifier ¶
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.