Documentation
¶
Overview ¶
Package appauth is the server side of the native app's sign-in (iOS design §2): the app never handles a password or an IdP token. It opens the server's own web sign-in in a system browser sheet and receives a one-time code, bound to its PKCE challenge, which it exchanges for a session token.
app: verifier, challenge = S256(verifier)
browser: GET /api/auth/app/start?code_challenge=… → Begin (state cookie)
… the usual OIDC sign-in …
GET /api/auth/app/finish (signed in, state cookie)
→ consent page ("Sign in the app as …?")
POST /api/auth/app/finish (state cookie + consent token)
→ Finish → 302 callback?code=…
app: POST /api/auth/app/token {code, code_verifier}
→ Redeem → user id
What makes it safe (DA's conditions on the design):
- the callback is fixed by the server, never taken from the request;
- the state cookie binds finish to the browser that started, and finish mints a code only after the signed-in user confirms on a same-origin page (a POST with a token derived from that cookie): a start link sent to someone else, opened in their already-signed-in browser, can't hand the sender a code without them pressing Continue;
- the callback is fixed per server, and rivolt.dev uses its https Universal Link, never the claimable rivolt:// scheme;
- the code lives 60 s, works once, and is only good with the verifier whose S256 is the challenge (a wrong verifier burns it);
- only HMACs of the state value and the code are stored.
Index ¶
- Constants
- Variables
- func Challenge(verifier string) string
- func ValidChallenge(c string) bool
- type Store
- func (s *Store) Begin(ctx context.Context, challenge string) (string, error)
- func (s *Store) CheckConsent(state, token string) bool
- func (s *Store) ConsentToken(state string) string
- func (s *Store) Finish(ctx context.Context, state string, uid uuid.UUID) (string, error)
- func (s *Store) Purge(ctx context.Context) (int64, error)
- func (s *Store) Redeem(ctx context.Context, code, verifier string) (uuid.UUID, error)
Constants ¶
const ( // StartTTL is how long a started sign-in may take to come back to // finish (the user signs in with the IdP in between). StartTTL = 10 * time.Minute // CodeTTL is how long the one-time code is good for: the app redeems it // immediately after the browser sheet closes. CodeTTL = 60 * time.Second )
const MaxOutstanding = 1000
MaxOutstanding caps the sign-ins started and not yet finished or expired, across all users: start is unauthenticated, so this bounds what a flood of starts can put in the table (DA on #769).
Variables ¶
var ErrBusy = errors.New("appauth: too many sign-ins in progress")
ErrBusy is Begin refusing because MaxOutstanding sign-ins are in flight.
var ErrInvalid = errors.New("appauth: invalid or expired")
ErrInvalid covers every way a start value, code or verifier can be wrong: unknown, expired, already used, or not matching. Callers can't tell which, so neither can an attacker.
Functions ¶
func ValidChallenge ¶
ValidChallenge reports whether c looks like an S256 PKCE challenge.
Types ¶
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store keeps sign-in attempts in app_auth_requests.
func New ¶
New returns a Store. pepper is the session-token HMAC key (the cookie secret), so state values and codes are stored like session tokens.
func (*Store) Begin ¶
Begin records a sign-in attempt for challenge and returns the raw state value the caller puts in the browser's state cookie.
func (*Store) CheckConsent ¶
CheckConsent reports whether token is ConsentToken(state).
func (*Store) ConsentToken ¶
ConsentToken is the CSRF token for the consent form shown at finish: an HMAC of the browser's state value, so only a page that saw this browser's state cookie can produce it. The code is minted only on a POST carrying it (DA on #769: finish never redirects with a code on a GET).
func (*Store) Finish ¶
Finish binds the signed-in user to the attempt the browser's state value names and returns the one-time code for the app. Each attempt yields one code: a second Finish (a replayed finish URL) fails.