### Install Fastly CLI (macOS) Source: https://context7.com/fastly/compute-rust-auth/llms.txt Installs the Fastly CLI using Homebrew on macOS. This is a prerequisite for local development and deployment. ```sh # 1. Install the Fastly CLI brew install fastly/tap/fastly # macOS ``` -------------------------------- ### Start Local Compute Server Source: https://github.com/fastly/compute-rust-auth/blob/main/README.md Starts the local development server for your Fastly Compute service. Use this to test your configuration and OAuth flow locally. ```term fastly compute serve ``` -------------------------------- ### Publish Compute Service Source: https://github.com/fastly/compute-rust-auth/blob/main/README.md Builds and publishes your Compute service after running through the setup configuration. You will be prompted for origin and IdP hostnames, and secrets for the secret and config stores. ```bash fastly compute publish ``` -------------------------------- ### Construct OAuth2 Authorization Request (Rust) Source: https://context7.com/fastly/compute-rust-auth/llms.txt Builds a `GET` request to the IdP's authorization endpoint with `AuthCodePayload` as URL query parameters. Ensure all required parameters like `client_id`, `code_challenge`, and `redirect_uri` are correctly set. ```rust let authorize_req = Request::get(&settings.openid_configuration.authorization_endpoint) .with_query(&AuthCodePayload { client_id: &settings.config.client_id, // "YOUR_CLIENT_ID" code_challenge: &pkce.code_challenge, // "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM" code_challenge_method: &settings.config.code_challenge_method, // "S256" redirect_uri: "https://your-service.edgecompute.app/callback", response_type: "code", scope: &settings.config.scope, // "openid" state: &state_and_nonce, // HS256 JWT token nonce: &nonce, // random nonce value }) .unwrap(); ``` -------------------------------- ### idp::AuthCodePayload Source: https://context7.com/fastly/compute-rust-auth/llms.txt Represents the query parameters for an IdP authorization request. These parameters are serialized as URL query parameters when making a `GET` request to the IdP's authorization endpoint. ```APIDOC ## AuthCodePayload — Query parameters for the IdP authorization request ### Description Serialized as URL query parameters in the `GET` redirect to the IdP's `authorization_endpoint`. ### Fields - `client_id` (string): Your application's client ID. - `code_challenge` (string): The code challenge generated for PKCE. - `code_challenge_method` (string): The method used to generate the code challenge (e.g., "S256"). - `redirect_uri` (string): The URI to redirect back to after authorization. - `response_type` (string): The type of response requested (typically "code"). - `scope` (string): The requested OAuth scopes (e.g., "openid"). - `state` (string): A value used to maintain state between the request and callback. - `nonce` (string): A random value used to mitigate replay attacks. ### Request Example ```rust use idp::AuthCodePayload; use fastly::Request; let authorize_req = Request::get(&settings.openid_configuration.authorization_endpoint) .with_query(&AuthCodePayload { client_id: &settings.config.client_id, // "YOUR_CLIENT_ID" code_challenge: &pkce.code_challenge, // "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM" code_challenge_method: &settings.config.code_challenge_method, // "S256" redirect_uri: "https://your-service.edgecompute.app/callback", response_type: "code", scope: &settings.config.scope, // "openid" state: &state_and_nonce, // HS256 JWT token nonce: &nonce, // random nonce value }) .unwrap(); ``` ### Example URL ```http GET https://accounts.google.com/o/oauth2/v2/auth ?client_id=YOUR_CLIENT_ID &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM &code_challenge_method=S256 &redirect_uri=https%3A%2F%2Fyour-service.edgecompute.app%2Fcallback &response_type=code &scope=openid &state=eyJ0eXAiOiJKV1Qi... &nonce=n-0S6_WzA2Mj ``` ``` -------------------------------- ### Fetch OIDC Discovery Document Source: https://github.com/fastly/compute-rust-auth/blob/main/README.md Fetches the OpenID Connect discovery document from Google's authorization server. This is used to configure the starter kit. ```sh curl -s https://accounts.google.com/.well-known/openid-configuration | jq -c @json ``` -------------------------------- ### Pkce::new(method) Source: https://context7.com/fastly/compute-rust-auth/llms.txt Generates a Proof Key for Code Exchange (PKCE) code verifier and code challenge. Supports 'S256' (SHA-256 hash, URL-safe base64 no-pad) and 'plain' encoding methods. ```APIDOC ## Pkce::new(method) ### Description Generate a PKCE code verifier and code challenge. Generates a cryptographically random 43-character `code_verifier` (per RFC 7636) and derives the corresponding `code_challenge`. Supports `"S256"` (SHA-256 hash, URL-safe base64 no-pad) and plain encoding. ### Usage ```rust use pkce::Pkce; // Generate PKCE pair using S256 (recommended): let pkce = Pkce::new("S256"); // pkce.code_verifier → "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk" (43 random alphanumeric chars) // pkce.code_challenge → "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM" (SHA-256 of verifier, base64url) // Generate PKCE pair using plain method: let pkce_plain = Pkce::new("plain"); // pkce_plain.code_verifier → same random string // pkce_plain.code_challenge → same value as code_verifier (plain encoding) // Use in the authorization request query: // ?code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM // &code_challenge_method=S256 // Later, send code_verifier in the token exchange so the IdP can verify. ``` ``` -------------------------------- ### Generate PKCE Code Verifier and Challenge with Pkce::new() Source: https://context7.com/fastly/compute-rust-auth/llms.txt Generates a PKCE code verifier and its corresponding code challenge. Supports 'S256' (recommended) and 'plain' encoding methods. The code verifier is a random string, and the challenge is derived from it. ```rust use pkce::Pkce; // Generate PKCE pair using S256 (recommended): let pkce = Pkce::new("S256"); // pkce.code_verifier → "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk" (43 random alphanumeric chars) // pkce.code_challenge → "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM" (SHA-256 of verifier, base64url) // Generate PKCE pair using plain method: let pkce_plain = Pkce::new("plain"); // pkce_plain.code_verifier → same random string // pkce_plain.code_challenge → same value as code_verifier (plain encoding) // Use in the authorization request query: // ?code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM // &code_challenge_method=S256 // Later, send code_verifier in the token exchange so the IdP can verify. ``` -------------------------------- ### Create Secret Files Source: https://context7.com/fastly/compute-rust-auth/llms.txt Creates necessary secret files for local development, such as client ID, client secret, and nonce secret. These files should be added to `.gitignore` to prevent accidental commits. ```sh # 2. Create secret files (gitignored) echo -n "YOUR_GOOGLE_CLIENT_ID" > .secret.client_id echo -n "YOUR_GOOGLE_CLIENT_SECRET" > .secret.client_secret dd if=/dev/random bs=32 count=1 | base64 > .secret.nonce_secret ``` -------------------------------- ### Create Nonce Token Signer/Verifier Source: https://context7.com/fastly/compute-rust-auth/llms.txt Initializes a `NonceToken` by deriving an HS256 key from a secret. This is used for generating and verifying short-lived HMAC-authenticated state tokens. ```rust use jwt::NonceToken; let nt = NonceToken::new(&settings.config.nonce_secret); // nonce_secret is SHA-256 hashed internally → HS256Key ``` -------------------------------- ### Config::load() Source: https://context7.com/fastly/compute-rust-auth/llms.txt Loads all service configuration from Fastly's Secret Store and Config Store. This includes client IDs, secrets, OIDC discovery documents, and JWKS. It returns a `Config` struct containing all necessary settings for the OAuth flow. ```APIDOC ## Config::load() ### Description Loads all service configuration from Fastly stores. Reads secrets from the `oauth_secrets` Secret Store and metadata from the `oauth_config` Config Store. Returns a `Config` struct containing the `ServiceConfiguration`, parsed `OpenIdConfiguration`, and `Jwks`. Called once per request at startup. ### Usage ```rust use fastly::{ConfigStore, SecretStore}; // Required secrets (in Fastly Secret Store "oauth_secrets"): // client_id — OAuth 2.0 client identifier // nonce_secret — random secret for nonce/CSRF token signing // client_secret — (optional) for IdPs like Google that require it // Required config (in Fastly Config Store "oauth_config"): // openid_configuration — JSON-stringified OIDC discovery document // jwks — JSON-stringified JSON Web Key Set // Optional config keys with defaults: // callback_path → "/callback" // scope → "openid" // code_challenge_method → "S256" // introspect_access_token → false // jwt_access_token → false let settings = Config::load().expect("Could not load configuration."); // settings.config.client_id → "YOUR_CLIENT_ID" // settings.config.callback_path → "/callback" // settings.config.scope → "openid" // settings.openid_configuration.issuer → "https://accounts.google.com" // settings.openid_configuration.authorization_endpoint → "https://accounts.google.com/o/oauth2/v2/auth" // settings.openid_configuration.token_endpoint → "https://oauth2.googleapis.com/token" // settings.openid_configuration.userinfo_endpoint → "https://openidconnect.googleapis.com/v1/userinfo" // settings.jwks.keys[0].key_id → "2af90e87be140c20038898a6efa11283dab6031d" ``` ``` -------------------------------- ### Build 307 Redirect Response with Cookies (Rust) Source: https://context7.com/fastly/compute-rust-auth/llms.txt Use this to build a 307 Temporary Redirect response. It sets four `Set-Cookie` headers from provided cookie strings. Useful for initiating auth flows with session cookies or completing them with token cookies. ```rust return Ok(responses::temporary_redirect( authorize_req.get_url_str(), // "https://accounts.google.com/o/oauth2/v2/auth? மூல... cookies::expired("access_token"), // clear any stale token cookies::expired("id_token"), // clear any stale token cookies::session("code_verifier", &pkce.code_verifier), cookies::session("state", &state), )); ``` ```rust return Ok(responses::temporary_redirect( "/articles/kittens", cookies::persistent("access_token", &auth.access_token, auth.expires_in), cookies::persistent("id_token", &auth.id_token, auth.expires_in), cookies::expired("code_verifier"), // clean up PKCE cookie cookies::expired("state"), // clean up state cookie )); ``` -------------------------------- ### Load Service Configuration with Config::load() Source: https://context7.com/fastly/compute-rust-auth/llms.txt Loads all necessary service configuration, including secrets and metadata, from Fastly's Secret Store and Config Store. This function is intended to be called once per request at startup. Ensure required secrets and config keys are present in the respective Fastly stores. ```rust use fastly::{ConfigStore, SecretStore}; // Required secrets (in Fastly Secret Store "oauth_secrets"): // client_id — OAuth 2.0 client identifier // nonce_secret — random secret for nonce/CSRF token signing // client_secret — (optional) for IdPs like Google that require it // Required config (in Fastly Config Store "oauth_config"): // openid_configuration — JSON-stringified OIDC discovery document // jwks — JSON-stringified JSON Web Key Set // Optional config keys with defaults: // callback_path → "/callback" // scope → "openid" // code_challenge_method → "S256" // introspect_access_token → false // jwt_access_token → false let settings = Config::load().expect("Could not load configuration."); // settings.config.client_id → "YOUR_CLIENT_ID" // settings.config.callback_path → "/callback" // settings.config.scope → "openid" // settings.openid_configuration.issuer → "https://accounts.google.com" // settings.openid_configuration.authorization_endpoint → "https://accounts.google.com/o/oauth2/v2/auth" // settings.openid_configuration.token_endpoint → "https://oauth2.googleapis.com/token" // settings.openid_configuration.userinfo_endpoint → "https://openidconnect.googleapis.com/v1/userinfo" // settings.jwks.keys[0].key_id → "2af90e87be140c20038898a6efa11283dab6031d" // Populate fastly.toml for local dev: // [local_server.config_stores.oauth_config.contents] // openid_configuration = "" // jwks = "" // callback_path = "/callback" // scope = "openid" // introspect_access_token = "true" // jwt_access_token = "false" // code_challenge_method = "S256" // Fetch and stringify Google's OIDC discovery doc: // curl -s https://accounts.google.com/.well-known/openid-configuration | jq -c @json // Fetch and stringify Google's JWKS: // curl -s https://www.googleapis.com/oauth2/v3/certs | jq -c @json ``` -------------------------------- ### Generate Signed State+Nonce Token Source: https://context7.com/fastly/compute-rust-auth/llms.txt Creates a 5-minute HS256 JWT containing the OAuth state and a random nonce. The token is sent as the `state` parameter to the IdP, and the nonce is sent as the `nonce` parameter. ```rust let state = "/articles/kittens?page=2aB3xZ9qR2k"; let (state_and_nonce, nonce) = NonceToken::new(&settings.config.nonce_secret) .generate_from_state(&state); // state_and_nonce → "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." (HS256 JWT, valid 5 min) // nonce → "n-0S6_WzA2Mj" (random value, sent to IdP, must come back in id_token) // Use in the authorization redirect URL: // GET https://accounts.google.com/o/oauth2/v2/auth // ?client_id=YOUR_CLIENT_ID // &response_type=code // &scope=openid // &redirect_uri=https://your-service.edgecompute.app/callback // &state=eyJ0eXAi... ← state_and_nonce // &nonce=n-0S6_WzA2Mj ← nonce // &code_challenge=E9Mel... // &code_challenge_method=S256 ``` -------------------------------- ### NonceToken::new(secret) Source: https://context7.com/fastly/compute-rust-auth/llms.txt Initializes a `NonceToken` struct, which is used for generating and verifying short-lived, HMAC-authenticated state tokens. It derives an HS256 key by SHA-256 hashing the provided secret. ```APIDOC ## NonceToken::new(secret) ### Description Creates a nonce token signer/verifier. Derives an HS256 key from the `nonce_secret` by SHA-256 hashing the secret bytes. The resulting `NonceToken` is used to generate and verify short-lived (5-minute) HMAC-authenticated state tokens that encode the OAuth state and a random nonce, preventing CSRF and replay attacks. ### Parameters * **secret** (&str) - The secret key used for signing and verifying tokens. ### Returns * `NonceToken` - An instance of the `NonceToken` struct. ### Example ```rust use jwt::NonceToken; let nt = NonceToken::new(&settings.config.nonce_secret); // nonce_secret is SHA-256 hashed internally → HS256Key ``` -------------------------------- ### Perform OAuth2 Token Exchange (Rust) Source: https://context7.com/fastly/compute-rust-auth/llms.txt Constructs a `POST` request to the IdP's token endpoint to exchange an authorization code for tokens. The `client_secret` is omitted if not provided. This uses `application/x-www-form-urlencoded` for the body. ```rust let mut exchange_res = Request::post(&settings.openid_configuration.token_endpoint) .with_body_form(&ExchangePayload { client_id: &settings.config.client_id, client_secret: settings.config.client_secret.as_deref(), // Some("SECRET") or None code: &qs.code, // authorization code from ?code= query param code_verifier: code_verifier, // PKCE verifier from session cookie grant_type: "authorization_code", redirect_uri: "https://your-service.edgecompute.app/callback", }) .unwrap() .send(config::IDP_BACKEND_NAME)?; ``` -------------------------------- ### NonceToken::generate_from_state(state) Source: https://context7.com/fastly/compute-rust-auth/llms.txt Generates a signed HS256 JWT (valid for 5 minutes) that includes the provided OAuth state string as the subject (`sub`) and attaches a random nonce. It returns both the generated token and the nonce. ```APIDOC ## NonceToken::generate_from_state(state) ### Description Generates a signed state+nonce token. Creates a 5-minute JWT signed with HS256 whose `sub` is the OAuth state string, and attaches a random nonce. Returns `(state_and_nonce_token, nonce)`. ### Parameters * **state** (&str) - The original OAuth state string. ### Returns * `(String, String)` - A tuple containing the signed token and the generated nonce. ### Example ```rust let state = "/articles/kittens?page=2aB3xZ9qR2k"; let (state_and_nonce, nonce) = NonceToken::new(&settings.config.nonce_secret) .generate_from_state(&state); // state_and_nonce → "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." (HS256 JWT, valid 5 min) // nonce → "n-0S6_WzA2Mj" (random value, sent to IdP, must come back in id_token) // Use in the authorization redirect URL: // GET https://accounts.google.com/o/oauth2/v2/auth // ?client_id=YOUR_CLIENT_ID // &response_type=code // &scope=openid // &redirect_uri=https://your-service.edgecompute.app/callback // &state=eyJ0eXAi... ← state_and_nonce // &nonce=n-0S6_WzA2Mj ← nonce // &code_challenge=E9Mel... // &code_challenge_method=S256 ``` -------------------------------- ### Fetch JWKS Document Source: https://github.com/fastly/compute-rust-auth/blob/main/README.md Fetches the JSON Web Key Set (JWKS) document from Google's certificate endpoint. This is used for JWT verification. ```sh curl -s https://www.googleapis.com/oauth2/v3/certs | jq -c @json ``` -------------------------------- ### cookies::session Source: https://context7.com/fastly/compute-rust-auth/llms.txt Creates a session-scoped `Set-Cookie` header string (no `Max-Age`) with standard security attributes. Ideal for temporary values like `code_verifier` and `state`. ```APIDOC ## cookies::session(name, value) ### Description Creates a session-scoped `Set-Cookie` header string with no `Max-Age` (deleted on browser close), with the same security attributes. Used to store `code_verifier` and `state` during the authorization flow. ### Parameters - **name** (string) - The name of the cookie. - **value** (string) - The value of the cookie. ### Returns A `Set-Cookie` header string. ### Example ```rust let cookie_str = cookies::session("code_verifier", "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"); // "__Secure-code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk; Path=/; SameSite=Lax; Secure; HttpOnly" ``` ``` -------------------------------- ### Parse Cookie Header in Rust Source: https://context7.com/fastly/compute-rust-auth/llms.txt Splits a raw `Cookie` header string into a `HashMap`. Automatically strips environment-specific prefixes like `__Secure-` or `local-` from keys. Useful for extracting authentication tokens from incoming requests. ```rust use cookies; let raw = "__Secure-access_token=eyJhbGc...; __Secure-id_token=eyJraWQ..."; let map = cookies::parse(raw); // map["access_token"] → "eyJhbGc..." // map["id_token"] → "eyJraWQ..." // In main.rs: let cookie_header = req.remove_header_str("cookie").unwrap_or_default(); let cookie = cookies::parse(&cookie_header); if let (Some(access_token), Some(id_token)) = (cookie.get("access_token"), cookie.get("id_token")) { // Both tokens present — proceed to validation } ``` -------------------------------- ### Verify State Token and Recover Nonce Source: https://context7.com/fastly/compute-rust-auth/llms.txt Verifies the HS256 signature and expiry of a state token returned by the IdP. It ensures the `sub` claim matches the expected state and returns the embedded nonce. ```rust let state_cookie = "/articles/kittens?page=2aB3xZ9qR2k"; // from state cookie let qs_state = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."; // from IdP callback ?state= match NonceToken::new(&settings.config.nonce_secret) .verify_and_claim_nonce(qs_state, state_cookie) { Some(nonce) => { // nonce → "n-0S6_WzA2Mj" // Proceed to exchange the authorization code, and pass nonce to // validate_token_rs256 to verify the id_token's nonce claim. println!("State verified; nonce recovered: {}", nonce); } None => { return Ok(responses::unauthorized("Could not verify state.")); } } ``` -------------------------------- ### Create Persistent Set-Cookie Header in Rust Source: https://context7.com/fastly/compute-rust-auth/llms.txt Generates a `Set-Cookie` header string for a named cookie with a specified `Max-Age`. Includes standard security attributes like `Path=/`, `SameSite=Lax`, `Secure`, and `HttpOnly`. Suitable for storing tokens that require persistence beyond a single session. ```rust use cookies; let cookie_str = cookies::persistent("access_token", "eyJhbGc...", 3600); // In production: // "__Secure-access_token=eyJhbGc...; Max-Age=3600; Path=/; SameSite=Lax; Secure; HttpOnly" // In local dev (FASTLY_SERVICE_VERSION=0): // "local-access_token=eyJhbGc...; Max-Age=3600; Path=/; SameSite=Lax; Secure; HttpOnly" ``` -------------------------------- ### cookies::parse Source: https://context7.com/fastly/compute-rust-auth/llms.txt Parses a raw `Cookie` header string into a `HashMap<&str, &str>`, automatically stripping environment-specific prefixes like `__Secure-` or `local-` from keys. ```APIDOC ## cookies::parse(cookie_header) ### Description Splits the raw `Cookie` header string into a `HashMap<&str, &str>`. Strips the environment-specific cookie prefix (`__Secure-` in production, `local-` in development) from each key automatically. ### Parameters - **cookie_header** (string) - The raw `Cookie` header string to parse. ### Returns A `HashMap<&str, &str>` where keys are cookie names (prefix stripped) and values are cookie values. ### Example ```rust let raw = "__Secure-access_token=eyJhbGc...; __Secure-id_token=eyJraWQ..."; let map = cookies::parse(raw); // map["access_token"] → "eyJhbGc..." // map["id_token"] → "eyJraWQ..." ``` ``` -------------------------------- ### Generate Nonce Secret Source: https://github.com/fastly/compute-rust-auth/blob/main/README.md Generates a random nonce secret for secure OAuth flows. Store this in a gitignored file. ```sh dd if=/dev/random bs=32 count=1 | base64 > .secret.nonce_secret ``` -------------------------------- ### Create Session-Scoped Set-Cookie Header in Rust Source: https://context7.com/fastly/compute-rust-auth/llms.txt Creates a session-scoped `Set-Cookie` header string without a `Max-Age`, ensuring the cookie is deleted when the browser closes. Includes the same security attributes as persistent cookies. Ideal for temporary values like `code_verifier` and `state` during authentication flows. ```rust use cookies; let cookie_str = cookies::session("code_verifier", "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"); // "__Secure-code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk; Path=/; SameSite=Lax; Secure; HttpOnly" ``` -------------------------------- ### NonceToken::verify_and_claim_nonce(token, expected_state) Source: https://context7.com/fastly/compute-rust-auth/llms.txt Verifies the HS256 signature and expiry of a state token received from an IdP. It also checks if the token's subject (`sub`) matches the `expected_state`. If verification is successful, it returns the embedded nonce; otherwise, it returns `None`. ```APIDOC ## NonceToken::verify_and_claim_nonce(token, expected_state) ### Description Verifies the HS256 signature and expiry of the state token returned by the IdP, requires its `sub` to match `expected_state` (the value stored in the state cookie), and returns the embedded nonce on success or `None` on failure. ### Parameters * **token** (&str) - The state token received from the IdP. * **expected_state** (&str) - The expected state value, typically retrieved from a cookie. ### Returns * `Option` - The recovered nonce if the token is valid and matches the expected state, otherwise `None`. ### Example ```rust let state_cookie = "/articles/kittens?page=2aB3xZ9qR2k"; // from state cookie let qs_state = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."; // from IdP callback ?state= match NonceToken::new(&settings.config.nonce_secret) .verify_and_claim_nonce(qs_state, state_cookie) { Some(nonce) => { // nonce → "n-0S6_WzA2Mj" // Proceed to exchange the authorization code, and pass nonce to // validate_token_rs256 to verify the id_token's nonce claim. println!("State verified; nonce recovered: {}", nonce); } None => { return Ok(responses::unauthorized("Could not verify state.")); } } ``` -------------------------------- ### cookies::persistent Source: https://context7.com/fastly/compute-rust-auth/llms.txt Creates a `Set-Cookie` header string for a named cookie with a specified `Max-Age` and standard security attributes (`Path=/; SameSite=Lax; Secure; HttpOnly`). ```APIDOC ## cookies::persistent(name, value, max_age) ### Description Creates a persistent `Set-Cookie` header value for a named cookie with a `Max-Age`, and the attributes `Path=/; SameSite=Lax; Secure; HttpOnly`. Used to store `access_token` and `id_token` after a successful token exchange. ### Parameters - **name** (string) - The name of the cookie. - **value** (string) - The value of the cookie. - **max_age** (integer) - The maximum age of the cookie in seconds. ### Returns A `Set-Cookie` header string. ### Example ```rust let cookie_str = cookies::persistent("access_token", "eyJhbGc...", 3600); // In production: // "__Secure-access_token=eyJhbGc...; Max-Age=3600; Path=/; SameSite=Lax; Secure; HttpOnly" // In local dev (FASTLY_SERVICE_VERSION=0): // "local-access_token=eyJhbGc...; Max-Age=3600; Path=/; SameSite=Lax; Secure; HttpOnly" ``` ``` -------------------------------- ### Create Expired Set-Cookie Header in Rust Source: https://context7.com/fastly/compute-rust-auth/llms.txt Generates a `Set-Cookie` header with `Max-Age=0` to immediately expire and clear a specified cookie. Useful for error handling or cleanup operations to ensure sensitive cookies are removed from the client. ```rust use cookies; let cookie_str = cookies::expired("state"); // "__Secure-state=expired; Max-Age=0; Path=/; SameSite=Lax; Secure; HttpOnly" // Clear all auth cookies on error: let res = responses::unauthorized("ID token invalid."); // Internally sets: expired("access_token"), expired("id_token"), // expired("code_verifier"), expired("state") ``` -------------------------------- ### Parse Token Exchange Response (Rust) Source: https://context7.com/fastly/compute-rust-auth/llms.txt Parses the JSON response from the token exchange request into an `AuthorizeResponse` struct. This struct typically contains `access_token`, `id_token`, and `expires_in` fields. ```rust let auth = exchange_res.take_body_json::().unwrap(); ``` -------------------------------- ### rand_chars(length) Source: https://context7.com/fastly/compute-rust-auth/llms.txt Generates a random alphanumeric string of a specified length. This is useful for creating unique identifiers like PKCE code verifiers and random suffixes for OAuth state parameters. ```APIDOC ## rand_chars(length) ### Description Generates a string of `length` random alphanumeric characters. ### Parameters * **length** (usize) - The desired length of the random string. ### Returns * `String` - A random alphanumeric string. ### Example ```rust use pkce::rand_chars; let suffix = rand_chars(10); // suffix → "aB3xZ9qR2k" ``` -------------------------------- ### responses::temporary_redirect Source: https://context7.com/fastly/compute-rust-auth/llms.txt Builds a 307 Temporary Redirect response with specified cookie headers. This function is used to initiate the authentication flow by redirecting to an Identity Provider (IdP) with session cookies, and to complete the flow by redirecting back to the original request with persistent token cookies. ```APIDOC ## responses::temporary_redirect(location, ...) ### Description Builds a 307 Temporary Redirect response with specified cookie headers. Used both to initiate the auth flow (redirecting to IdP with session cookies) and to complete it (redirecting to the original request with persistent token cookies). ### Method This is a Rust function call, not an HTTP method. ### Parameters - `location` (string): The URL to redirect to. - `...` (cookie strings): One or more cookie strings to set via `Set-Cookie` headers. ### Request Example ```rust // Initiating the auth flow — set session cookies, redirect to IdP: return Ok(responses::temporary_redirect( authorize_req.get_url_str(), // "https://accounts.google.com/o/oauth2/v2/auth?..." cookies::expired("access_token"), // clear any stale token cookies::expired("id_token"), // clear any stale token cookies::session("code_verifier", &pkce.code_verifier), cookies::session("state", &state), )); // Completing the auth flow — set persistent token cookies, redirect to original URL: return Ok(responses::temporary_redirect( "/articles/kittens", cookies::persistent("access_token", &auth.access_token, auth.expires_in), cookies::persistent("id_token", &auth.id_token, auth.expires_in), cookies::expired("code_verifier"), // clean up PKCE cookie cookies::expired("state"), // clean up state cookie )); ``` ### Response - Returns an HTTP 307 Temporary Redirect response. ### Response Example ```http HTTP/1.1 307 Temporary Redirect location: https://accounts.google.com/o/oauth2/v2/auth?client_id=...&state=... set-cookie: __Secure-code_verifier=dBjft...; Path=/; SameSite=Lax; Secure; HttpOnly set-cookie: __Secure-state=/articles/kittens...; Path=/; SameSite=Lax; Secure; HttpOnly ``` ``` -------------------------------- ### idp::ExchangePayload Source: https://context7.com/fastly/compute-rust-auth/llms.txt Represents the form body for the authorization code token exchange. This payload is posted as `application/x-www-form-urlencoded` to the IdP's token endpoint. ```APIDOC ## ExchangePayload — Form body for the authorization code token exchange ### Description Posted as `application/x-www-form-urlencoded` to the IdP's `token_endpoint`. The `client_secret` field is omitted from serialization if `None`. ### Fields - `client_id` (string): Your application's client ID. - `client_secret` (string, optional): Your application's client secret. Omitted if `None`. - `code` (string): The authorization code received from the IdP. - `code_verifier` (string): The PKCE code verifier. - `grant_type` (string): The grant type, typically "authorization_code". - `redirect_uri` (string): The URI used during the authorization request. ### Request Example ```rust use idp::ExchangePayload; use fastly::Request; let mut exchange_res = Request::post(&settings.openid_configuration.token_endpoint) .with_body_form(&ExchangePayload { client_id: &settings.config.client_id, client_secret: settings.config.client_secret.as_deref(), // Some("SECRET") or None code: &qs.code, // authorization code from ?code= query param code_verifier: code_verifier, // PKCE verifier from session cookie grant_type: "authorization_code", redirect_uri: "https://your-service.edgecompute.app/callback", }) .unwrap() .send(config::IDP_BACKEND_NAME)?; ``` ### Example Request Body ```http POST https://oauth2.googleapis.com/token Content-Type: application/x-www-form-urlencoded client_id=YOUR_CLIENT_ID&client_secret=SECRET&code=4/P7q7W91a-oMsCeLvIaQm... &code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk &grant_type=authorization_code &redirect_uri=https%3A%2F%2Fyour-service.edgecompute.app%2Fcallback ``` ### Response Example (AuthorizeResponse) ```json { "access_token": "ya29.a0A...", "id_token": "eyJhbGc...", "expires_in": 3599 } ``` ### Accessing Response Fields ```rust let auth = exchange_res.take_body_json::().unwrap(); // auth.access_token → "ya29.a0A..." // auth.id_token → "eyJhbGc..." // auth.expires_in → 3599 ``` ``` -------------------------------- ### cookies::expired Source: https://context7.com/fastly/compute-rust-auth/llms.txt Generates a `Set-Cookie` header string with `Max-Age=0` to immediately expire and delete a specified cookie. Useful for error handling and cleanup. ```APIDOC ## cookies::expired(name) ### Description Creates an expired `Set-Cookie` header value to clear a cookie. Returns a `Set-Cookie` header string with `Max-Age=0` to immediately expire and delete the named cookie. Used in error responses and post-exchange cleanup. ### Parameters - **name** (string) - The name of the cookie to expire. ### Returns A `Set-Cookie` header string for expiring the cookie. ### Example ```rust let cookie_str = cookies::expired("state"); // "__Secure-state=expired; Max-Age=0; Path=/; SameSite=Lax; Secure; HttpOnly" // Clear all auth cookies on error: // let res = responses::unauthorized("ID token invalid."); // Internally sets: expired("access_token"), expired("id_token"), // expired("code_verifier"), expired("state") ``` ``` -------------------------------- ### Build 401 Unauthorized Response with Cookie Clearing in Rust Source: https://context7.com/fastly/compute-rust-auth/llms.txt Constructs an HTTP 401 Unauthorized response with a given body. It automatically appends `Set-Cookie` headers to expire all four authentication-related cookies (`access_token`, `id_token`, `code_verifier`, `state`). This is used when token validation fails. ```rust use responses; // On state mismatch: return Ok(responses::unauthorized("Could not verify state.")); // HTTP/1.1 401 Unauthorized // set-cookie: __Secure-access_token=expired; Max-Age=0; Path=/; SameSite=Lax; Secure; HttpOnly // set-cookie: __Secure-id_token=expired; Max-Age=0; Path=/; SameSite=Lax; Secure; HttpOnly // set-cookie: __Secure-code_verifier=expired; Max-Age=0; Path=/; SameSite=Lax; Secure; HttpOnly // set-cookie: __Secure-state=expired; Max-Age=0; Path=/; SameSite=Lax; Secure; HttpOnly // // Could not verify state. // Surface IdP error body directly: return Ok(responses::unauthorized(exchange_res.take_body())); ``` -------------------------------- ### Generate Random Alphanumeric String Source: https://context7.com/fastly/compute-rust-auth/llms.txt Generates a random alphanumeric string of a specified length. Useful for PKCE code verifiers and state parameter suffixes. ```rust use pkce::rand_chars; let suffix = rand_chars(10); // suffix → "aB3xZ9qR2k" (10 random alphanumeric chars) ``` -------------------------------- ### responses::unauthorized Source: https://context7.com/fastly/compute-rust-auth/llms.txt Constructs a 401 Unauthorized HTTP response with a given body and automatically appends `Set-Cookie` headers to clear all four authentication-related cookies. ```APIDOC ## responses::unauthorized(body) ### Description Builds a 401 Unauthorized response and clears cookies. Returns an HTTP 401 response with the provided body, and appends `Set-Cookie` headers to expire all four auth cookies (`access_token`, `id_token`, `code_verifier`, `state`). Used wherever token validation fails. ### Parameters - **body** (string) - The response body content. ### Returns A `fastly::http::Response` object configured as 401 Unauthorized with cookie clearing headers. ### Example ```rust // On state mismatch: return Ok(responses::unauthorized("Could not verify state.")); // HTTP/1.1 401 Unauthorized // set-cookie: __Secure-access_token=expired; Max-Age=0; Path=/; SameSite=Lax; Secure; HttpOnly // set-cookie: __Secure-id_token=expired; Max-Age=0; Path=/; SameSite=Lax; Secure; HttpOnly // set-cookie: __Secure-code_verifier=expired; Max-Age=0; Path=/; SameSite=Lax; Secure; HttpOnly // set-cookie: __Secure-state=expired; Max-Age=0; Path=/; SameSite=Lax; Secure; HttpOnly // // Could not verify state. // Surface IdP error body directly: // return Ok(responses::unauthorized(exchange_res.take_body())); ``` ``` -------------------------------- ### validate_token_rs256(token, settings, required_nonce) Source: https://context7.com/fastly/compute-rust-auth/llms.txt Verifies an RS256-signed JWT. It checks the signature against the JWKS, and validates the issuer, audience, expiry, and optionally a nonce claim. This function is used for validating both ID tokens and JWT access tokens. ```APIDOC ## validate_token_rs256(token, settings, required_nonce) ### Description Verifies an RS256-signed JWT's signature against the matching key in the JWKS (matched by `kid` header), and verifies `iss`, `aud`, expiry, and optionally the `nonce` claim. Used to validate both `id_token` and (when `jwt_access_token = true`) `access_token`. ### Parameters * **token** (&str) - The JWT string to validate. * **settings** (&Settings) - The settings object containing JWKS information. * **required_nonce** (Option<&str>) - An optional nonce string to verify against the `nonce` claim in the JWT. ### Returns * `Result` - On success, returns the claims within the JWT. On failure, returns an error. ### Example ```rust use jwt::{validate_token_rs256}; use jwt_simple::claims::NoCustomClaims; // Validate an ID token with nonce check: let nonce = "abc123-nonce-value"; match validate_token_rs256::(&auth.id_token, &settings, Some(&nonce)) { Ok(claims) => { println!("ID token valid for subject: {:?}", claims.subject); } Err(e) => { // Returns unauthorized response if invalid return Ok(responses::unauthorized("ID token invalid.")); } } // Validate a JWT access token (no nonce check): if validate_token_rs256::(access_token, &settings, None).is_err() { return Ok(responses::unauthorized("JWT access token invalid.")); } // Example with custom claims: #[derive(Serialize, Deserialize)] struct MyClaims { role: String } let claims = validate_token_rs256::(&token, &settings, None)?; println!("User role: {}", claims.custom.role); ``` -------------------------------- ### Validate RS256-Signed JWT Source: https://context7.com/fastly/compute-rust-auth/llms.txt Validates an RS256-signed JWT against a JWKS, checking signature, issuer, audience, expiry, and optionally a nonce claim. Use for ID tokens after auth code exchange or JWT access tokens. ```rust use jwt::{validate_token_rs256}; use jwt_simple::claims::NoCustomClaims; // Validate an ID token (with nonce check — required after the auth code exchange): let nonce = "abc123-nonce-value"; match validate_token_rs256::(&auth.id_token, &settings, Some(&nonce)) { Ok(claims) => { // claims.subject → Some("1234567890") (user's sub) // claims.issued_at → Some(UnixTimeStamp) // claims.expires_at → Some(UnixTimeStamp) println!("ID token valid for subject: {:?}", claims.subject); } Err(e) => { // Returns unauthorized response if invalid return Ok(responses::unauthorized("ID token invalid.")); } } // Validate a JWT access token at the edge (no nonce check): if validate_token_rs256::(access_token, &settings, None).is_err() { return Ok(responses::unauthorized("JWT access token invalid.")); } // Custom claims example: #[derive(Serialize, Deserialize)] struct MyClaims { role: String } let claims = validate_token_rs256::(&token, &settings, None)?; println!("User role: {}", claims.custom.role); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.