provider/authorization.aug diagrams
OpenID Connect login application
Project overview · Compiled explanation
Class interactions
authorize
View 1 of 2
Mermaid source
flowchart LR
n0["securityHeaders"]
n1["withCookie"]
n2["settings"]
n3["ExpiringStore"]
n4["Crypto"]
n5["Clock"]
n6["authorize"]
n6 -->|"calls"| n0
n6 -->|"calls"| n1
n6 -->|"calls"| n2
n6 -->|"calls put; depends on"| n3
n6 -->|"calls decodeBase64url; calls random; depends on"| n4
n6 -->|"calls now; depends on"| n5
View 2 of 2
Mermaid source
flowchart LR
n0["authorize"]
n1["providerLogin"]
n2["ProviderLogin"]
n0 -->|"defers HTTP call to"| n1
n0 -->|"calls"| n2
providerLogin
View 1 of 2
Mermaid source
flowchart LR
n0["securityHeaders"]
n1["withCookie"]
n2["settings"]
n3["ExpiringStore"]
n4["urlEncode"]
n5["Crypto"]
n6["providerLogin"]
n6 -->|"calls"| n0
n6 -->|"calls"| n1
n6 -->|"calls"| n2
n6 -->|"calls put; calls take; depends on"| n3
n6 -->|"calls"| n4
n6 -->|"calls equal; calls random; depends on"| n5
View 2 of 2
Mermaid source
flowchart LR
n0["Clock"]
n1["providerLogin"]
n2["verifyCredentials"]
n3["ProviderFailure"]
n1 -->|"calls now; depends on"| n0
n1 -->|"calls"| n2
n1 -->|"calls"| n3
A browser action defers HTTP call to its handler until submission.
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.
authorize
authorize handles GET /provider/authorize.
Validate the registered client before offering a login form. A malformed redirect is never followed.
It reads response_type, client_id, redirect_uri, scope, state, nonce, code_challenge, and code_challenge_method from the HTTP query. scope is called requestedScope here. It gets crypto (Crypto), clock (Clock), and requests (ExpiringStore<AuthorizationRequest>) from dependency injection.
It can call Crypto.decodeBase64url, Crypto.random, Clock.now, and ExpiringStore<AuthorizationRequest>.put.
The handler responds with HTTP 400 for LoginError, 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 authorize
participant p1 as common/settings
participant p2 as crypto: Crypto
Note over p0: GET /provider/authorize
p0->>p1: settings()
p1-->>p0: config: Settings
alt client_id does not equal config.clientId or redirect_uri<br/>does not equal config.callback or response_type does not<br/>equal ”code” or code_challenge_method does not equal<br/>”S256”
p0->>p0: LoginError() · construct value
p0-->>p0: LoginError result: LoginError
Note over p0: Raise checked failure LoginError(); required cleanup<br/>runs before exit
end
alt requestedScope does not equal ”openid” and<br/>requestedScope does not equal ”openid profile”
p0->>p0: LoginError() · construct value
p0-->>p0: LoginError result 2: LoginError
Note over p0: Raise checked failure LoginError(); required cleanup<br/>runs before exit
end
p0->>p0: state.isToken(min=43, max=128)
p0-->>p0: isToken result: bool
opt (not isToken result) is false
p0->>p0: nonce.isToken(min=43, max=128)
p0-->>p0: isToken result 2: bool
end
opt (not isToken result or not isToken result 2) is false
p0->>p0: code_challenge.length()
p0-->>p0: length result: int
end
alt state is not a URL-safe ASCII token with 43 to 128<br/>characters or nonce is not a URL-safe ASCII token with<br/>43 to 128 characters or the byte length of<br/>code_challenge does not equal 43
p0->>p0: LoginError() · construct value
p0-->>p0: LoginError result 3: LoginError
Note over p0: Raise checked failure LoginError(); required cleanup<br/>runs before exit
end
opt Try body; stops on a checked failure
p0->>p2: decodeBase64url(input=code_challenge) · interface<br/>dispatch
p2-->>p0: decodeBase64url result: Bytes
p0->>p0: decodeBase64url result.length()
p0-->>p0: length result 2: int
alt the byte length of crypto.decodeBase64url with input<br/>from code_challenge does not equal 32
p0->>p0: LoginError() · construct value
p0-->>p0: LoginError result 4: LoginError
end
end
Sequence 2 of 3 (continued)
Mermaid source
sequenceDiagram
participant p0 as authorize
participant p1 as crypto: Crypto
participant p2 as clock: Clock
participant p3 as requests: ExpiringStore
participant p4 as common/headers
opt Try body; stops on a checked failure
alt the byte length of crypto.decodeBase64url with input<br/>from code_challenge does not equal 32
Note over p0: Sequence continued from the previous view
Note over p0: Raise checked failure LoginError(); required cleanup<br/>runs before exit
end
end
opt Catch CryptoError
p0->>p0: LoginError() · construct value
p0-->>p0: LoginError result 5: LoginError
Note over p0: Raise checked failure LoginError(); required cleanup<br/>runs before exit
end
p0->>p1: random(size=32) · interface dispatch
p1-->>p0: random result: Bytes
p0->>p0: random result.base64url()
p0-->>p0: requestId: string
p0->>p1: random(size=32) · interface dispatch
p1-->>p0: random result 2: Bytes
p0->>p0: random result 2.base64url()
p0-->>p0: browser: string
p0->>p1: random(size=32) · interface dispatch
p1-->>p0: random result 3: Bytes
p0->>p0: random result 3.base64url()
p0-->>p0: csrf: string
p0->>p2: now() · interface dispatch
p2-->>p0: now: int
p0->>p0: AuthorizationRequest(clientId=client_id,<br/>redirectUri=redirect_uri, state=state, nonce=nonce,<br/>challenge=code_challenge, browser=browser, csrf=csrf,<br/>expires=now + 300) · construct value
p0-->>p0: request: AuthorizationRequest
p0->>p3: put(key=requestId, value=request,<br/>expires=request.expires, now=now) · interface dispatch
p0->>p4: securityHeaders()
p4-->>p0: securityHeaders result: Headers
Sequence 3 of 3 (continued)
Mermaid source
sequenceDiagram
participant p0 as authorize
participant p1 as common/headers
participant p2 as provider/views
Note over p0: Sequence continued from the previous view
p0->>p1: withCookie(headers=securityHeaders result,<br/>name=”aug_authorize”, value=browser, path=”/provider”,<br/>maxAge=300, secure=config.secureCookies)
p1-->>p0: headers: Headers
Note over p0: Create browser action for POST /provider/login; called<br/>on submission
p0->>p2: ProviderLogin(requestId=requestId, csrf=csrf,<br/>message=”Authorize the registered August login app.”,<br/>submit=handle providerLogin(input from form))
p2-->>p0: ProviderLogin result: Html
p0->>p0: HttpResponse(body=ProviderLogin result, headers=headers)
p0-->>p0: HttpResponse result: HttpResponse‹Html›
Note over p0: Return HttpResponse(body=ProviderLogin(requestId, csrf,<br/>message=”Authorize the registered August login app.”,<br/>submit=handle providerLogin(input from form)),<br/>headers=headers); required cleanup runs before exit
Note over p0: May leave with checked errors: CryptoError, HttpError,<br/>LoginError, StoreFull, TimeError
Note over p0: HTTP result follows declared response and error mapping;<br/>unhandled request failure returns 500
providerLogin
providerLogin handles POST /provider/login.
The browser binding and CSRF token are checked before credentials. Each form request is consumed once.
It takes form as LoginForm from the HTTP form, browser as optional string from the HTTP cookie aug_authorize, and origin as optional string from the HTTP header origin. It gets crypto (Crypto), clock (Clock), requests (ExpiringStore<AuthorizationRequest>), and codes (ExpiringStore<AuthorizationCode>) from dependency injection. Omitted optional inputs are null.
It can call Clock.now, ExpiringStore<AuthorizationRequest>.take, Crypto.equal, Crypto.random, ExpiringStore<AuthorizationCode>.put, Crypto.passwordHash, and Crypto.decodeBase64url.
The handler responds with HTTP 503 for CryptoError, HTTP 503 for TimeError, and HTTP 503 for StoreFull.
It can also raise HttpError.
Sequence 1 of 5
Mermaid source
sequenceDiagram
participant p0 as providerLogin
participant p1 as common/settings
participant p2 as provider/views
participant p3 as common/headers
participant p4 as clock: Clock
participant p5 as requests: ExpiringStore
Note over p0: POST /provider/login
p0->>p1: settings()
p1-->>p0: config: Settings
opt Try body; stops on a checked failure
alt origin does not equal config.baseUrl
p0->>p2: ProviderFailure(message=”The sign-in form must come from<br/>this app.”)
p2-->>p0: ProviderFailure result: Html
p0->>p3: securityHeaders()
p3-->>p0: securityHeaders result: Headers
p0->>p0: HttpResponse(body=ProviderFailure result, status=403,<br/>headers=securityHeaders result)
p0-->>p0: HttpResponse result: HttpResponse‹Html›
Note over p0: Return HttpResponse(body=ProviderFailure(message=”The<br/>sign-in form must come from this app.”), status=403,<br/>headers=securityHeaders()); required cleanup runs before<br/>exit
end
p0->>p4: now() · interface dispatch
p4-->>p0: now result: int
p0->>p5: take(key=form.request_id, now=now result) · interface<br/>dispatch
p5-->>p0: take result: optional AuthorizationRequest
alt Match when null:
p0->>p2: ProviderFailure(message=”The sign-in request expired or<br/>was already used.”)
p2-->>p0: ProviderFailure result 2: Html
p0->>p3: securityHeaders()
p3-->>p0: securityHeaders result 2: Headers
p0->>p0: HttpResponse(body=ProviderFailure result 2, status=400,<br/>headers=securityHeaders result 2)
p0-->>p0: HttpResponse result 2: HttpResponse‹Html›
Note over p0: Return HttpResponse(body=ProviderFailure(message=”The<br/>sign-in request expired or was already used.”),<br/>status=400, headers=securityHeaders()); required cleanup<br/>runs before exit
else Match when some request:
alt Match when null:
p0->>p2: ProviderFailure(message=”The browser binding is<br/>missing.”)
p2-->>p0: ProviderFailure result 3: Html
end
end
end
Sequence 2 of 5 (continued)
Mermaid source
sequenceDiagram
participant p0 as providerLogin
participant p1 as common/headers
participant p2 as crypto: Crypto
participant p3 as provider/views
opt Try body; stops on a checked failure
alt Continuing Match when some request:
alt Match when null:
Note over p0: Sequence continued from the previous view
p0->>p1: securityHeaders()
p1-->>p0: securityHeaders result 3: Headers
p0->>p0: HttpResponse(body=ProviderFailure result 3, status=403,<br/>headers=securityHeaders result 3)
p0-->>p0: HttpResponse result 3: HttpResponse‹Html›
Note over p0: Return HttpResponse(body=ProviderFailure(message=”The<br/>browser binding is missing.”), status=403,<br/>headers=securityHeaders()); required cleanup runs before<br/>exit
else Match when some secret:
p0->>p0: secret.bytes()
p0-->>p0: bytes result: Bytes
p0->>p0: request.browser.bytes()
p0-->>p0: bytes result 2: Bytes
p0->>p2: equal(left=bytes result, right=bytes result 2) ·<br/>interface dispatch
p2-->>p0: equal result: bool
opt (not equal result) is false
p0->>p0: form.csrf.bytes()
p0-->>p0: bytes result 3: Bytes
p0->>p0: request.csrf.bytes()
p0-->>p0: bytes result 4: Bytes
p0->>p2: equal(left=bytes result 3, right=bytes result 4) ·<br/>interface dispatch
p2-->>p0: equal result 2: bool
end
alt crypto.equal with left from the UTF-8 bytes of secret<br/>and right from the UTF-8 bytes of request.browser<br/>returns false or crypto.equal with left from the UTF-8<br/>bytes of form.csrf and right from the UTF-8 bytes of<br/>request.csrf returns false
p0->>p3: ProviderFailure(message=”The sign-in form could not be<br/>verified.”)
p3-->>p0: ProviderFailure result 4: Html
p0->>p1: securityHeaders()
p1-->>p0: securityHeaders result 4: Headers
p0->>p0: HttpResponse(body=ProviderFailure result 4, status=403,<br/>headers=securityHeaders result 4)
p0-->>p0: HttpResponse result 4: HttpResponse‹Html›
end
end
end
end
Sequence 3 of 5 (continued)
Mermaid source
sequenceDiagram
participant p0 as providerLogin
participant p1 as provider/credentials
participant p2 as provider/views
participant p3 as common/headers
participant p4 as clock: Clock
participant p5 as crypto: Crypto
opt Try body; stops on a checked failure
alt Continuing Match when some request:
alt Continuing Match when some secret:
alt crypto.equal with left from the UTF-8 bytes of secret<br/>and right from the UTF-8 bytes of request.browser<br/>returns false or crypto.equal with left from the UTF-8<br/>bytes of form.csrf and right from the UTF-8 bytes of<br/>request.csrf returns false
Note over p0: Sequence continued from the previous view
Note over p0: Return HttpResponse(body=ProviderFailure(message=”The<br/>sign-in form could not be verified.”), status=403,<br/>headers=securityHeaders()); required cleanup runs before<br/>exit
end
end
p0->>p1: verifyCredentials(username=form.username,<br/>password=form.password)
p1-->>p0: verifyCredentials result: bool
alt verifyCredentials with form.username and form.password<br/>using injected crypto returns false
p0->>p2: ProviderFailure(message=”The username or password was<br/>not accepted.”)
p2-->>p0: ProviderFailure result 5: Html
p0->>p3: securityHeaders()
p3-->>p0: securityHeaders result 5: Headers
p0->>p0: HttpResponse(body=ProviderFailure result 5, status=401,<br/>headers=securityHeaders result 5)
p0-->>p0: HttpResponse result 5: HttpResponse‹Html›
Note over p0: Return HttpResponse(body=ProviderFailure(message=”The<br/>username or password was not accepted.”), status=401,<br/>headers=securityHeaders()); required cleanup runs before<br/>exit
end
p0->>p4: now() · interface dispatch
p4-->>p0: now: int
p0->>p5: random(size=32) · interface dispatch
p5-->>p0: random result: Bytes
p0->>p0: random result.base64url()
p0-->>p0: code: string
p0->>p0: AuthorizationCode(clientId=request.clientId,<br/>redirectUri=request.redirectUri,<br/>challenge=request.challenge, nonce=request.nonce,<br/>subject=”demo-ada”, name=”Ada”, expires=now + 60) ·<br/>construct value
p0-->>p0: grant: AuthorizationCode
end
end
Sequence 4 of 5 (continued)
Mermaid source
sequenceDiagram
participant p0 as providerLogin
participant p1 as codes: ExpiringStore
participant p2 as web/contracts
participant p3 as common/headers
participant p4 as provider/views
opt Try body; stops on a checked failure
alt Continuing Match when some request:
Note over p0: Sequence continued from the previous view
p0->>p1: put(key=code, value=grant, expires=grant.expires,<br/>now=now) · interface dispatch
p0->>p2: urlEncode(input=code)
p2-->>p0: urlEncode result: string
p0->>p2: urlEncode(input=request.state)
p2-->>p0: urlEncode result 2: string
Note over p0: Set location to request.redirectUri + ”?code=” +<br/>urlEncode result + ”&state=” + urlEncode result 2
p0->>p3: securityHeaders()
p3-->>p0: securityHeaders result 6: Headers
p0->>p0: securityHeaders result 6.with(name=”location”,<br/>value=location)
p0-->>p0: with result: Headers
p0->>p3: withCookie(headers=with result, name=”aug_authorize”,<br/>value=””, path=”/provider”, maxAge=0,<br/>secure=config.secureCookies)
p3-->>p0: headers: Headers
p0->>p0: HttpResponse(body=‹p›Returning to the application.‹/p›,<br/>status=303, headers=headers)
p0-->>p0: HttpResponse result 6: HttpResponse‹Html›
Note over p0: Return HttpResponse(body=‹p›Returning to the<br/>application.‹/p›, status=303, headers=headers); required<br/>cleanup runs before exit
end
end
opt Catch HttpError
p0->>p4: ProviderFailure(message=”The submitted form is<br/>invalid.”)
p4-->>p0: ProviderFailure result 6: Html
p0->>p3: securityHeaders()
p3-->>p0: securityHeaders result 7: Headers
p0->>p0: HttpResponse(body=ProviderFailure result 6, status=400,<br/>headers=securityHeaders result 7)
p0-->>p0: HttpResponse result 7: HttpResponse‹Html›
Note over p0: Return HttpResponse(body=ProviderFailure(message=”The<br/>submitted form is invalid.”), status=400,<br/>headers=securityHeaders()); required cleanup runs before<br/>exit
end
Note over p0: May leave with checked errors: CryptoError, HttpError,<br/>StoreFull, TimeError
Sequence 5 of 5 (continued)
Mermaid source
sequenceDiagram
participant p0 as providerLogin
Note over p0: Sequence continued from the previous view
Note over p0: HTTP result follows declared response and error mapping;<br/>unhandled request failure returns 500
Called contracts
- securityHeaders — common/headers.aug
- withCookie — common/headers.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
- 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.decodeBase64url — 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
- 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
- providerLogin — provider/authorization.aug
- AuthorizationCode — provider/contracts.aug
- AuthorizationRequest — provider/contracts.aug
- LoginError — provider/contracts.aug
- verifyCredentials — provider/credentials.aug
- ProviderFailure — provider/views.aug
- ProviderLogin — provider/views.aug