client/login.aug diagrams
OpenID Connect login application
Project overview · Compiled explanation
Class interactions
loginCallback
View 1 of 3
Mermaid source
flowchart LR
n0["loginCallback"]
n1["discover"]
n2["responseJson"]
n3["validateIdentity"]
n4["securityHeaders"]
n5["withCookie"]
n6["SigningKeys"]
n0 -->|"calls"| n1
n0 -->|"calls"| n2
n0 -->|"calls"| n3
n0 -->|"calls"| n4
n0 -->|"calls"| n5
n0 -->|"calls session; depends on"| n6
View 2 of 3
Mermaid source
flowchart LR
n0["loginCallback"]
n1["settings"]
n2["ExpiringStore"]
n3["HttpClient"]
n4["urlEncode"]
n5["Crypto"]
n6["signJwt"]
n0 -->|"calls"| n1
n0 -->|"calls put; calls take; depends on"| n2
n0 -->|"calls request; depends on"| n3
n0 -->|"calls"| n4
n0 -->|"calls equal; calls random; depends on"| n5
n0 -->|"calls"| n6
View 3 of 3
Mermaid source
flowchart LR
n0["loginCallback"]
n1["Clock"]
n0 -->|"calls now; depends on"| n1
startLogin
View 1 of 2
Mermaid source
flowchart LR
n0["startLogin"]
n1["discover"]
n2["securityHeaders"]
n3["withCookie"]
n4["settings"]
n5["ExpiringStore"]
n6["HttpClient"]
n0 -->|"calls"| n1
n0 -->|"calls"| n2
n0 -->|"calls"| n3
n0 -->|"calls"| n4
n0 -->|"calls put; depends on"| n5
n0 -->|"depends on"| n6
View 2 of 2
Mermaid source
flowchart LR
n0["startLogin"]
n1["urlEncode"]
n2["Crypto"]
n3["Clock"]
n0 -->|"calls"| n1
n0 -->|"calls random; calls sha256; depends on"| n2
n0 -->|"calls now; depends on"| n3
Sequences
Call arrows identify checked targets; loop and branch frames determine when they run. Open that target’s module to follow its implementation. Branches describe alternatives; loops describe repeated work. Native calls and interface dispatch stop at their declared contracts. Exit notes end that path; enclosing recovery and cleanup remain visible.
startLogin
startLogin handles GET /login/start.
Start a browser-bound, short-lived transaction. The PKCE verifier stays on the server.
It gets crypto (Crypto), clock (Clock), client (HttpClient), and transactions (ExpiringStore<LoginTransaction>) from dependency injection.
It can call Crypto.random, Clock.now, ExpiringStore<LoginTransaction>.put, Crypto.sha256, and HttpClient.request.
The handler responds with HTTP 502 for SessionError, HTTP 503 for CryptoError, HTTP 503 for TimeError, and HTTP 503 for StoreFull.
It can also raise HttpError.
Sequence 1 of 3
Mermaid source
sequenceDiagram
participant p0 as startLogin
participant p1 as common/settings
participant p2 as client/protocol
participant p3 as crypto: Crypto
participant p4 as clock: Clock
Note over p0: GET /login/start
p0->>p1: settings()
p1-->>p0: config: Settings
p0->>p2: discover()
p2-->>p0: document: Discovery
p0->>p3: random(size=32) · interface dispatch
p3-->>p0: random result: Bytes
p0->>p0: random result.base64url()
p0-->>p0: browser: string
p0->>p3: random(size=32) · interface dispatch
p3-->>p0: random result 2: Bytes
p0->>p0: random result 2.base64url()
p0-->>p0: state: string
p0->>p3: random(size=32) · interface dispatch
p3-->>p0: random result 3: Bytes
p0->>p0: random result 3.base64url()
p0-->>p0: nonce: string
p0->>p3: random(size=32) · interface dispatch
p3-->>p0: random result 4: Bytes
p0->>p0: random result 4.base64url()
p0-->>p0: verifier: string
p0->>p4: now() · interface dispatch
p4-->>p0: now result: int
Sequence 2 of 3 (continued)
Mermaid source
sequenceDiagram
participant p0 as startLogin
participant p1 as clock: Clock
participant p2 as transactions: ExpiringStore
participant p3 as crypto: Crypto
participant p4 as web/contracts
Note over p0: Sequence continued from the previous view
p0->>p0: LoginTransaction(state=state, nonce=nonce,<br/>verifier=verifier, expires=now result + 300) · construct<br/>value
p0-->>p0: transaction: LoginTransaction
p0->>p1: now() · interface dispatch
p1-->>p0: now result 2: int
p0->>p2: put(key=browser, value=transaction,<br/>expires=transaction.expires, now=now result 2) ·<br/>interface dispatch
p0->>p0: verifier.bytes()
p0-->>p0: bytes result: Bytes
p0->>p3: sha256(input=bytes result) · interface dispatch
p3-->>p0: sha256 result: Bytes
p0->>p0: sha256 result.base64url()
p0-->>p0: challenge: string
p0->>p4: urlEncode(input=config.clientId)
p4-->>p0: urlEncode result: string
p0->>p4: urlEncode(input=config.callback)
p4-->>p0: urlEncode result 2: string
p0->>p4: urlEncode(input=state)
p4-->>p0: urlEncode result 3: string
p0->>p4: urlEncode(input=nonce)
p4-->>p0: urlEncode result 4: string
p0->>p4: urlEncode(input=challenge)
p4-->>p0: urlEncode result 5: string
Note over p0: Set location to document.authorization_endpoint +<br/>”?response_type=code&client_id=” + urlEncode result +<br/>”&redirect_uri=” + urlEncode result 2 +<br/>”&scope=openid%20profile&state=” + urlEncode result 3 +<br/>”&nonce=” + urlEncode result 4 + ”&code_challenge=” +<br/>urlEncode result 5 + ”&code_challenge_method=S256”
Sequence 3 of 3 (continued)
Mermaid source
sequenceDiagram
participant p0 as startLogin
participant p1 as common/headers
Note over p0: Sequence continued from the previous view
p0->>p1: securityHeaders()
p1-->>p0: securityHeaders result: Headers
p0->>p0: securityHeaders result.with(name=”location”,<br/>value=location)
p0-->>p0: with result: Headers
p0->>p1: withCookie(headers=with result, name=”aug_login”,<br/>value=browser, path=”/login”, maxAge=300,<br/>secure=config.secureCookies)
p1-->>p0: headers: Headers
p0->>p0: HttpResponse(body=‹p›Opening the identity provider.‹/p›,<br/>status=303, headers=headers)
p0-->>p0: HttpResponse result: HttpResponse‹Html›
Note over p0: Return HttpResponse(body=‹p›Opening the identity<br/>provider.‹/p›, status=303, headers=headers); required<br/>cleanup runs before exit
Note over p0: May leave with checked errors: CryptoError, HttpError,<br/>SessionError, StoreFull, TimeError
Note over p0: HTTP result follows declared response and error mapping;<br/>unhandled request failure returns 500
loginCallback
loginCallback handles GET /login/callback.
Exchange a one-use code over HTTP, verify the provider JWT/JWKS and UserInfo subject, then issue a distinct app-session JWT.
It takes code and state as strings from the HTTP query and browser as optional string from the HTTP cookie aug_login. It gets crypto (Crypto), clock (Clock), client (HttpClient), keys (SigningKeys), transactions (ExpiringStore<LoginTransaction>), and sessions (ExpiringStore<SessionClaims>) from dependency injection. Omitted optional inputs are null.
It can call Clock.now, ExpiringStore<LoginTransaction>.take, Crypto.equal, HttpClient.request, Crypto.random, SigningKeys.session, ExpiringStore<SessionClaims>.put, Crypto.signRsa, Crypto.decodeBase64url, Crypto.importRsa, and Crypto.verifyRsa.
The handler responds with HTTP 400 for SessionError, HTTP 503 for CryptoError, HTTP 503 for TimeError, and HTTP 503 for StoreFull.
It can also raise HttpError, JsonError, JwtError, and KeyError.
Sequence 1 of 5
Mermaid source
sequenceDiagram
participant p0 as loginCallback
participant p1 as clock: Clock
participant p2 as transactions: ExpiringStore
participant p3 as crypto: Crypto
Note over p0: GET /login/callback
p0->>p0: code.isToken(min=43, max=43)
p0-->>p0: isToken result: bool
opt (not isToken result) is false
p0->>p0: state.isToken(min=43, max=43)
p0-->>p0: isToken result 2: bool
end
alt code is not a URL-safe ASCII token with 43 to 43<br/>characters or state is not a URL-safe ASCII token with<br/>43 to 43 characters
p0->>p0: SessionError() · construct value
p0-->>p0: SessionError result: SessionError
Note over p0: Raise checked failure SessionError(); required cleanup<br/>runs before exit
end
alt Match when null:
p0->>p0: SessionError() · construct value
p0-->>p0: SessionError result 2: SessionError
Note over p0: Raise checked failure SessionError(); required cleanup<br/>runs before exit
else Match when some secret:
p0->>p1: now() · interface dispatch
p1-->>p0: now result: int
p0->>p2: take(key=secret, now=now result) · interface dispatch
p2-->>p0: take result: optional LoginTransaction
alt Match when null:
p0->>p0: SessionError() · construct value
p0-->>p0: SessionError result 3: SessionError
Note over p0: Raise checked failure SessionError(); required cleanup<br/>runs before exit
else Match when some transaction:
p0->>p0: transaction.state.bytes()
p0-->>p0: bytes result: Bytes
p0->>p0: state.bytes()
p0-->>p0: bytes result 2: Bytes
p0->>p3: equal(left=bytes result, right=bytes result 2) ·<br/>interface dispatch
p3-->>p0: equal result: bool
end
end
Sequence 2 of 5 (continued)
Mermaid source
sequenceDiagram
participant p0 as loginCallback
participant p1 as common/settings
participant p2 as client/protocol
participant p3 as web/contracts
alt Continuing Match when some secret:
alt Continuing Match when some transaction:
alt crypto.equal with left from the UTF-8 bytes of<br/>transaction.state and right from the UTF-8 bytes of<br/>state returns false
Note over p0: Sequence continued from the previous view
p0->>p0: SessionError() · construct value
p0-->>p0: SessionError result 4: SessionError
Note over p0: Raise checked failure SessionError(); required cleanup<br/>runs before exit
end
p0->>p1: settings()
p1-->>p0: config: Settings
p0->>p2: discover()
p2-->>p0: document: Discovery
p0->>p3: urlEncode(input=code)
p3-->>p0: urlEncode result: string
p0->>p3: urlEncode(input=config.callback)
p3-->>p0: urlEncode result 2: string
p0->>p3: urlEncode(input=config.clientId)
p3-->>p0: urlEncode result 3: string
p0->>p3: urlEncode(input=transaction.verifier)
p3-->>p0: urlEncode result 4: string
Note over p0: Set body to ”grant_type=authorization_code&code=” +<br/>urlEncode result + ”&redirect_uri=” + urlEncode result 2<br/>+ ”&client_id=” + urlEncode result 3 + ”&code_verifier=”<br/>+ urlEncode result 4
p0->>p0: Headers()
p0-->>p0: Headers result: Headers
p0->>p0: Headers result.with(name=”content-type”,<br/>value=”application/x-www-form-urlencoded”)
p0-->>p0: headers: Headers
p0->>p0: body.bytes()
p0-->>p0: bytes result 3: Bytes
end
end
Sequence 3 of 5 (continued)
Mermaid source
sequenceDiagram
participant p0 as loginCallback
participant p1 as client: HttpClient
participant p2 as client/protocol
participant p3 as clock: Clock
alt Continuing Match when some secret:
alt Continuing Match when some transaction:
Note over p0: Sequence continued from the previous view
p0->>p1: request(method=”POST”, url=document.token_endpoint,<br/>headers=headers, body=bytes result 3) · interface<br/>dispatch
p1-->>p0: request result: HttpResponse‹Bytes›
p0->>p2: responseJson(response=request result)
p2-->>p0: responseJson result: Json
p0->>p0: responseJson result.decode‹TokenResponse›()
p0-->>p0: tokens: TokenResponse
opt (tokens.token_type != ”Bearer”) is false
p0->>p0: tokens.access_token.isToken(min=43, max=43)
p0-->>p0: isToken result 3: bool
end
alt tokens.token_type does not equal ”Bearer” or<br/>tokens.access_token is not a URL-safe ASCII token with<br/>43 to 43 characters or tokens.expires_in is at most 0
p0->>p0: SessionError() · construct value
p0-->>p0: SessionError result 5: SessionError
Note over p0: Raise checked failure SessionError(); required cleanup<br/>runs before exit
end
p0->>p1: request(method=”GET”, url=document.jwks_uri) · interface<br/>dispatch
p1-->>p0: request result 2: HttpResponse‹Bytes›
p0->>p2: responseJson(response=request result 2)
p2-->>p0: responseJson result 2: Json
p0->>p0: responseJson result 2.decode‹RsaJwks›()
p0-->>p0: jwks: RsaJwks
p0->>p3: now() · interface dispatch
p3-->>p0: now result 2: int
p0->>p2: validateIdentity(token=tokens.id_token,<br/>nonce=transaction.nonce, now=now result 2, jwks=jwks)
p2-->>p0: identity: IdClaims
p0->>p0: Headers()
p0-->>p0: Headers result 2: Headers
end
end
Sequence 4 of 5 (continued)
Mermaid source
sequenceDiagram
participant p0 as loginCallback
participant p1 as client: HttpClient
participant p2 as client/protocol
participant p3 as clock: Clock
participant p4 as crypto: Crypto
alt Continuing Match when some secret:
alt Continuing Match when some transaction:
Note over p0: Sequence continued from the previous view
p0->>p0: Headers result 2.with(name=”authorization”,<br/>value=”Bearer ” + tokens.access_token)
p0-->>p0: authHeaders: Headers
p0->>p1: request(method=”GET”, url=document.userinfo_endpoint,<br/>headers=authHeaders) · interface dispatch
p1-->>p0: request result 3: HttpResponse‹Bytes›
p0->>p2: responseJson(response=request result 3)
p2-->>p0: responseJson result 3: Json
p0->>p0: responseJson result 3.decode‹UserInfo›()
p0-->>p0: user: UserInfo
alt user.sub does not equal identity.sub
p0->>p0: SessionError() · construct value
p0-->>p0: SessionError result 6: SessionError
Note over p0: Raise checked failure SessionError(); required cleanup<br/>runs before exit
end
p0->>p3: now() · interface dispatch
p3-->>p0: now: int
p0->>p4: random(size=32) · interface dispatch
p4-->>p0: random result: Bytes
p0->>p0: random result.base64url()
p0-->>p0: base64url result: string
p0->>p4: random(size=32) · interface dispatch
p4-->>p0: random result 2: Bytes
p0->>p0: random result 2.base64url()
p0-->>p0: base64url result 2: string
p0->>p0: SessionClaims(iss=config.baseUrl + ”/app”,<br/>sub=identity.sub, aud=”august-app”, exp=now +<br/>config.sessionSeconds, iat=now, jti=base64url result,<br/>csrf=base64url result 2, name=user.name) · construct<br/>value
p0-->>p0: session: SessionClaims
end
end
Sequence 5 of 5 (continued)
Mermaid source
sequenceDiagram
participant p0 as loginCallback
participant p1 as keys: SigningKeys
participant p2 as crypto/jose
participant p3 as sessions: ExpiringStore
participant p4 as common/headers
alt Continuing Match when some secret:
alt Continuing Match when some transaction:
Note over p0: Sequence continued from the previous view
p0->>p1: session() · interface dispatch
p1-->>p0: session result: RsaPrivateKey
p0->>p0: Json(value=session)
p0-->>p0: Json result: Json
p0->>p2: signJwt(key=session result, claims=Json result,<br/>kid=”session-1”, tokenType=”august-session+jwt”)
p2-->>p0: jwt: string
p0->>p3: put(key=session.jti, value=session, expires=session.exp,<br/>now=now) · interface dispatch
p0->>p4: securityHeaders()
p4-->>p0: securityHeaders result: Headers
p0->>p0: securityHeaders result.with(name=”location”, value=”/”)
p0-->>p0: with result 3: Headers
p0->>p4: withCookie(headers=with result 3, name=”aug_session”,<br/>value=jwt, path=”/”, maxAge=config.sessionSeconds,<br/>secure=config.secureCookies)
p4-->>p0: responseHeaders: Headers
p0->>p4: withCookie(headers=responseHeaders, name=”aug_login”,<br/>value=””, path=”/login”, maxAge=0,<br/>secure=config.secureCookies)
p4-->>p0: responseHeaders: Headers
p0->>p0: HttpResponse(body=‹p›Signed in.‹/p›, status=303,<br/>headers=responseHeaders)
p0-->>p0: HttpResponse result: HttpResponse‹Html›
Note over p0: Return HttpResponse(body=‹p›Signed in.‹/p›, status=303,<br/>headers=responseHeaders); required cleanup runs before<br/>exit
end
end
Note over p0: May leave with checked errors: CryptoError, HttpError,<br/>JsonError, JwtError, KeyError, SessionError, StoreFull,<br/>TimeError
Note over p0: HTTP result follows declared response and error mapping;<br/>unhandled request failure returns 500
Called contracts
- LoginTransaction — client/contracts.aug
- SessionClaims — client/contracts.aug
- SessionError — client/contracts.aug
- discover — client/protocol.aug
- responseJson — client/protocol.aug
- validateIdentity — client/protocol.aug
- securityHeaders — common/headers.aug
- withCookie — common/headers.aug
- SigningKeys — common/keys.aug
- SigningKeys.session — common/keys.aug
- settings — common/settings.aug
- ExpiringStore — package/@git/url_0eb7c89453c87681ed15@0.0.0-git.a39fc582565d4fca40be4f75fa304d71adc69301/store.aug
- ExpiringStore.put — package/@git/url_0eb7c89453c87681ed15@0.0.0-git.a39fc582565d4fca40be4f75fa304d71adc69301/store.aug
- ExpiringStore.take — package/@git/url_0eb7c89453c87681ed15@0.0.0-git.a39fc582565d4fca40be4f75fa304d71adc69301/store.aug
- HttpClient — package/@git/url_897efafd565158fc4908@0.0.0-git.a39fc582565d4fca40be4f75fa304d71adc69301/contracts.aug
- HttpClient.request — package/@git/url_897efafd565158fc4908@0.0.0-git.a39fc582565d4fca40be4f75fa304d71adc69301/contracts.aug
- urlEncode — package/@git/url_897efafd565158fc4908@0.0.0-git.a39fc582565d4fca40be4f75fa304d71adc69301/contracts.aug
- Crypto — package/@git/url_9ef654c66d34ab8f5527@0.0.0-git.b14a0f9aa41f1ce58bd51133bcdc424033e40d40/contracts.aug
- Crypto.equal — package/@git/url_9ef654c66d34ab8f5527@0.0.0-git.b14a0f9aa41f1ce58bd51133bcdc424033e40d40/contracts.aug
- Crypto.random — package/@git/url_9ef654c66d34ab8f5527@0.0.0-git.b14a0f9aa41f1ce58bd51133bcdc424033e40d40/contracts.aug
- Crypto.sha256 — package/@git/url_9ef654c66d34ab8f5527@0.0.0-git.b14a0f9aa41f1ce58bd51133bcdc424033e40d40/contracts.aug
- signJwt — package/@git/url_9ef654c66d34ab8f5527@0.0.0-git.b14a0f9aa41f1ce58bd51133bcdc424033e40d40/jose.aug
- Clock — package/@git/url_c092cd151499c4e1d8a1@0.0.0-git.a39fc582565d4fca40be4f75fa304d71adc69301/contracts.aug
- Clock.now — package/@git/url_c092cd151499c4e1d8a1@0.0.0-git.a39fc582565d4fca40be4f75fa304d71adc69301/contracts.aug