provider/userinfo.aug
OpenID Connect login application · Source and specification
Files in this project
main.augclient/contracts.augclient/endpoints.augclient/export.augclient/login.augclient/logout.augclient/protocol.augclient/session.augclient/views.augcommon/export.augcommon/headers.augcommon/keys.augcommon/settings.augcommon/views.augprovider/authorization.augprovider/contracts.augprovider/credentials.augprovider/discovery.augprovider/export.augprovider/token.augprovider/userinfo.augprovider/views.aug
Code
// aug-spec: "userinfo.aug.md" explains this file. Read it before changes; refresh with aug spec.
import UserInfo and AccessGrant from contracts
import securityHeaders from common
import Clock from time
import ExpiringStore from memory
/** The opaque access token is valid only at this provider. Missing, expired and malformed credentials receive the same response. */
endpoint GET "/provider/userinfo" as userinfo(optional string authorization from header, resolve Clock clock, resolve ExpiringStore<AccessGrant> access):
match authorization:
when null:
pass
when some header:
parts = header.split(separator=" ")
if parts.length() == 2:
try:
if parts.get(index=0) == "Bearer":
token = parts.get(index=1)
if token.isToken(min=43, max=43):
match access.get(key=token, now=clock.now()):
when null:
pass
when some grant:
return HttpResponse(
body=Json(
value=UserInfo(sub=grant.subject, name=grant.name)
),
headers=securityHeaders()
)
catch IndexError error:
pass
headers = securityHeaders().with(
name="www-authenticate",
value="Bearer error=\"invalid_token\""
)
return HttpResponse(
body=Json(value={"error": "invalid_token"}),
status=401,
headers=headers
)// aug-spec: "userinfo.aug.md" explains this file. Read it before changes; refresh with aug spec.
import UserInfo and AccessGrant from contracts
import securityHeaders from common
import Clock from time
import ExpiringStore from memory
/** The opaque access token is valid only at this provider. Missing, expired and malformed credentials receive the same response. */
endpoint GET "/provider/userinfo" as userinfo(optional string authorization from header, resolve Clock clock, resolve ExpiringStore<AccessGrant> access) {
match authorization {
when null {
pass
}
when some header {
parts = header.split(separator=" ")
if parts.length() == 2 {
try {
if parts.get(index=0) == "Bearer" {
token = parts.get(index=1)
if token.isToken(min=43, max=43) {
match access.get(key=token, now=clock.now()) {
when null {
pass
}
when some grant {
return HttpResponse(
body=Json(
value=UserInfo(sub=grant.subject, name=grant.name)
),
headers=securityHeaders()
)
}
}
}
}
}
catch IndexError error {
pass
}
}
}
}
headers = securityHeaders().with(
name="www-authenticate",
value="Bearer error=\"invalid_token\""
)
return HttpResponse(
body=Json(value={"error": "invalid_token"}),
status=401,
headers=headers
)
}Compiled specification
Plain handler results default to HTTP 200 unless another status is declared. HttpResponse values choose their own status. Unhandled request failures return HTTP 500 and cancel the request tasks.
userinfo · source
userinfo handles GET /provider/userinfo. The opaque access token is valid only at this provider. Missing, expired and malformed credentials receive the same response.
It takes authorization as optional string from the HTTP header. It gets clock (Clock) and access (ExpiringStore<AccessGrant>) from dependency injection. Omitted optional inputs are null. It can call Clock.now and ExpiringStore<AccessGrant>.get.
It can also raise HttpError and TimeError.
If authorization is null, it continues without an operation. If authorization is not null, using header for it sets parts to header.split with separator " ". If the number of elements in parts equals 2, if the item at index 0 in parts equals "Bearer", it sets token to the item at index 1 in parts. If token is a URL-safe ASCII token with 43 to 43 characters, if access.get with key from token and now from clock.now is null, it continues without an operation. source
If access.get with key from token and now from clock.now is not null, using grant for it returns HTTP 200 with a Json with value from an UserInfo with sub from grant.subject and grant.name and securityHeaders headers. If this work raises IndexError, it continues without an operation. It sets headers to securityHeaders with the header "www-authenticate" set to "Bearer error=\"invalid_token\"". It returns HTTP 401 with a Json with value from a map with "error" mapped to "invalid_token" and headers headers. source
Checked interface
userinfo(optional string authorization, resolve Clock clock, resolve ExpiringStore<AccessGrant> access) returns HttpResponse<Json> unless HttpError and TimeError uses Clock.now, ExpiringStore<AccessGrant>.getIt takes authorization as optional string from the HTTP header. It gets clock (Clock) and access (ExpiringStore<AccessGrant>) from dependency injection. Omitted optional inputs are null. It can call Clock.now and ExpiringStore<AccessGrant>.get.
It can also raise HttpError and TimeError.
Dependencies
It uses ExpiringStore (get) from memory. It uses Clock (now) from time. It uses securityHeaders from common. It uses AccessGrant (name and subject) and UserInfo from contracts.
Built-in operations follow the language reference.