AuthController

struct AuthController : APIRouteCollection

The collection of /api/v3/auth/* route endpoints and handler functions related to authentication.

API v3 requires the use of either HTTP Basic Authentication (RFC7617) or HTTP Bearer Authentication (based on RFC6750) for virtually all endpoint access, with very few exceptions carved out for fully public data (such as the Event Schedule).

This means that essentially all HTTP requests must contain an Authorization header.

Important

The query-based &key= scheme used in v2 is not supported at all.

A valid HTTP Basic Authentication header resembles:

Authorization: Basic YWRtaW46cGFzc3dvcmQ=

The data value in a Basic header is the base64-encoded utf-8 string representation of the user’s username and password, separated by a colon. In Swift, a one-off version might resemble something along the lines of:

var request = URLRequest(…) let credentials = “username:password”.data(using: .utf8).base64encodedString() request.addValue(“Basic (credentials)”, forHTTPHeaderField: “Authorization”) …

Successful execution of sending this request to the login endpoint returns a JSON-encoded token string:

{ “token”: “y+jiK8w/7Ta21m/O8F2edw==” }

which is then used in HTTP Bearer Authentication for all subsequent requests:

Authorization: Bearer y+jiK8w/7Ta21m/O8F2edw==

A generated token string remains valid across all clients on all devices until the user explicitly logs out, or it otherwise expires or is administratively deleted. If the user explicitly logs out on any client on any device, the token is deleted and the /api/v3/auth/login endpoint will need to be hit again to generate a new one.

RouteCollection Conformance

Open Access Handlers

  • recoveryHandler(_:) Asynchronous

    POST /api/v3/auth/recovery

    Attempts to authorize the user using a combination of User.username and any one of the User.verification (registration code), User.password or User.recoveryKey (returned by UserController.createHandler(_:data:)) values.

    The use case is a forgotten password. While an API client has probably stored the information internally, that doesn’t necessarily help if the user is setting up another client or on another device, and is even less likely to be of use for logging into the web front end.

    Upon successful authentication, the user’s password is set to the newPassword value in the provided UserRecoveryData, the user is logged in, and the users’ login TokenStringData is returned (the same struct returned by /api/v3/login).

    Note

    The User.verification registration code can only be used to recover once. This limitation is to prevent a possible race condition in which a malicious user has obtained another’s registration code. After one successful recovery has been executed via the code, subsequent recovery can only be done via the recoveryKey provided during initial account creation.

    Note

    To prevent brute-force malicious attempts, there is a limit on successive failed recovery attempts, currently hard-coded to 5.

    Throws

    400 error if the recovery fails. 403 error if the maximum number of successive failed recovery attempts has been reached. A 5xx response should be reported as a likely bug, please and thank you.

    Declaration

    Swift

    func recoveryHandler(_ req: Request) async throws -> TokenStringData

    Parameters

    requestBody

    Return Value

    TokenStringData containing an authentication token (string) that should be used for all subsequent HTTP requests, until expiry or revocation.

basicAuthGroup Handlers (not logged in)

  • loginHandler(_:) Asynchronous

    POST /api/v3/auth/login

    Our basic login handler that utilizes the user’s username and password.

    The login credentials are expected to be provided using HTTP Basic Authentication. That is, a base64-encoded utf-8 string representation of the user’s username and password, separated by a colon (“username:password”), in the Authorization header of the POST request. For example:

    let credentials = “username:password”.data(using: .utf8).base64encodedString() request.addValue(“Basic (credentials)”, forHTTPHeaderField: “Authorization”)

    would generate an HTTP header of:

    Authorization: Basic YWRtaW46cGFzc3dvcmQ=

    There is no payload in the HTTP body; this header field carries all the necessary data. The token string returned by successful execution of this login handler

    { “token”: “y+jiK8w/7Ta21m/O8F2edw==” }

    is then used for HTTP Bearer Authentication in all subsequent requests:

    Authorization: Bearer y+jiK8w/7Ta21m/O8F2edw==

    In order to support the simultaneous use of multiple clients and/or devices by a single user, any existing token will be returned in lieu of generating a new one. A token will remain valid until the user explicitly logs out (or it otherwise expires or is administratively revoked), at which point this endpoint will need to be hit again to generate a new token.

    Note

    API v2 query parameter style logins and subsequent key submissions are not supported in API v3.

    Requires

    User.accessLevel other than .banned.

    Requires

    HTTP Basic Auth in Authorization header.

    Throws

    401 error if the Basic authentication fails. 403 error if the user is banned. A 5xx response should be reported as a likely bug, please and thank you.

    Declaration

    Swift

    func loginHandler(_ req: Request) async throws -> TokenStringData

    Return Value

    TokenStringData containing an authentication token (string) that should be used for all subsequent HTTP requests, until expiry or revocation.

tokenAuthGroup Handlers (logged in)

  • logoutHandler(_:) Asynchronous

    POST /api/v3/auth/logout

    Unauthenticates the user and deletes the user’s authentication token. It is the responsibility of the client to respond appropriately to the returned HTTPStatus, which should be one of:

    • 204 No Content
    • 401 Unauthorized {“error”: “true”, “reason”: “User not authenticated.”}
    • 409 Conflict { “error”: “true”, “reason”: “user is not logged in” }

    A 409 response most likely indicates a theoretically possible race condition. There should be no side effect and it is likely harmless, but please do report a 409 error if you encounter one so that the specifics can be looked into.

    Throws

    401 error if the authentication failed. 409 error if the user somehow wasn’t logged in.

    Declaration

    Swift

    func logoutHandler(_ req: Request) async throws -> HTTPStatus

    Return Value

    204 No Content if the token was successfully deleted.