A provider can live in its own package. This example starts a Twitch
strategy package that depends on Vestibule and the same OAuth helpers
used by the built-in strategies.
name = "vestibule_twitch"version = "0.1.0"description = "Twitch OAuth strategy for Vestibule"licences = ["MIT"]gleam = ">= 1.7.0"[dependencies]vestibule = ">= 1.0.0 and < 2.0.0"gleam_stdlib = ">= 0.48.0 and < 2.0.0"gleam_http = ">= 4.3.0 and < 5.0.0"gleam_httpc = ">= 5.0.0 and < 6.0.0"gleam_json = ">= 3.1.0 and < 4.0.0"glow_auth = ">= 1.0.1 and < 2.0.0"
2
Build the authorization URL
The function receives the already-resolved scopes and state value.
Return the provider URL; Vestibule appends the PKCE challenge.
Exchange the authorization code with the provider token endpoint,
parse the response into ExchangeResult, then use the
credentials and artifacts to fetch a stable provider UID and
normalized UserInfo.
fn do_exchange_code( cfg: Config, code: String, code_verifier: Option(String),) -> Result(strategy.ExchangeResult, AuthError(e)) { // Build and send the provider token request, then parse credentials. use body <- result.try(post_token_request(cfg, code, code_verifier)) provider_support.parse_oauth_token_response( body, provider_support.OptionalScope(" "), ) |> result.map(strategy.exchange_result)}fn do_fetch_user( _cfg: Config, exchange: strategy.ExchangeResult,) -> Result(strategy.UserResult, AuthError(e)) { use auth_header <- result.try( strategy.authorization_header(strategy.exchange_credentials(exchange)), ) use #(uid, info) <- result.try(provider_support.fetch_json_with_auth( "https://id.twitch.tv/oauth2/userinfo", auth_header, parse_user_response, "Twitch userinfo", )) Ok(strategy.user_result(uid: uid, info: info, extra: dict.new()))}
4
Expose the final strategy value
Export a strategy() function that returns
Strategy(e) when the provider only needs built-in error
variants, or Strategy(YourError) when it exposes custom
domain-specific errors.