DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
jwt.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file jwt.h
6 * @brief Zero-heap JWT (JSON Web Token) bearer-auth verification, HS256.
7 *
8 * Stateless request authentication: a client presents `Authorization: Bearer
9 * <jwt>` and the server verifies the token's HMAC-SHA-256 signature against a
10 * shared secret (reusing the SSH crypto layer's HMAC). No sessions, no per-client
11 * state, no heap - all work happens in fixed stack/BSS buffers and the whole core
12 * is host-testable (env:native_jwt).
13 *
14 * Only HS256 (HMAC-SHA-256) is supported - the deterministic, allocation-free
15 * choice for a constrained device sharing a secret with its issuer. RS256/ES256
16 * (asymmetric) are out of scope. Signature verification is constant-time.
17 *
18 * The base verifier (jwt_verify_hs256 / jwt_bearer_valid) checks only the
19 * signature. The `*_at` variants additionally enforce the RFC 7519 time claims
20 * (`exp` §4.1.4 and `nbf` §4.1.5) against a caller-supplied wall-clock epoch, with a
21 * skew leeway - pass now=0 on a clockless device to skip the time checks (the
22 * signature still gates). `iat` (§4.1.6) is informational; read it with
23 * jwt_claim_int() if you want it.
24 */
25
26#ifndef DETERMINISTICESPASYNCWEBSERVER_JWT_H
27#define DETERMINISTICESPASYNCWEBSERVER_JWT_H
28
29#include "ServerConfig.h"
30#include <stddef.h>
31#include <stdint.h>
32
33#if DETWS_ENABLE_JWT
34
35/**
36 * @brief Verify the HS256 signature of a JWT.
37 *
38 * Checks that the token has exactly the `header.payload.signature` shape and that
39 * base64url(HMAC-SHA-256(secret, "header.payload")) equals the signature segment
40 * (constant-time). Does not inspect claims.
41 *
42 * @param token the compact JWT string.
43 * @param token_len length of @p token (e.g. strlen).
44 * @param secret HMAC key bytes.
45 * @param secret_len key length.
46 * @return true if the signature is valid.
47 */
48bool jwt_verify_hs256(const char *token, size_t token_len, const uint8_t *secret, size_t secret_len);
49
50/**
51 * @brief Validate an `Authorization` header value carrying a Bearer JWT.
52 *
53 * Accepts a value beginning with `Bearer ` (case-insensitive scheme), then
54 * verifies the token via jwt_verify_hs256().
55 *
56 * @param auth_header the full Authorization header value (may be nullptr).
57 * @param secret HMAC key bytes.
58 * @param secret_len key length.
59 * @return true if a well-formed Bearer token validates.
60 */
61bool jwt_bearer_valid(const char *auth_header, const uint8_t *secret, size_t secret_len);
62
63/**
64 * @brief Check a JWT's time-based validity (RFC 7519 `exp` / `nbf`) against a clock.
65 *
66 * Enforces `exp` (§4.1.4: rejected once @p now_epoch has passed it) and `nbf`
67 * (§4.1.5: rejected before it), each within @p leeway_s seconds of skew tolerance.
68 * An absent claim is not enforced. Does NOT check the signature - pair with
69 * jwt_verify_hs256(). The comparisons are written to avoid integer overflow near the
70 * `long` epoch range.
71 *
72 * @param now_epoch current Unix time in seconds; <= 0 means "no wall clock", so the
73 * time claims cannot be evaluated and the function returns true (the
74 * signature check remains the gate).
75 * @param leeway_s allowed clock skew in seconds (0 for none).
76 * @return true if the token is currently within its validity window (or no clock is set).
77 */
78bool jwt_time_valid(const char *token, size_t token_len, long now_epoch, long leeway_s);
79
80/**
81 * @brief Verify a JWT's HS256 signature AND its `exp` / `nbf` time claims.
82 *
83 * jwt_verify_hs256() && jwt_time_valid(). On a clockless device (@p now_epoch <= 0)
84 * this reduces to the signature-only check.
85 */
86bool jwt_verify_hs256_at(const char *token, size_t token_len, const uint8_t *secret, size_t secret_len, long now_epoch,
87 long leeway_s);
88
89/**
90 * @brief Validate a Bearer `Authorization` header, enforcing `exp` / `nbf` when clocked.
91 *
92 * jwt_bearer_valid() plus the RFC 7519 time-claim check. Pass @p now_epoch from your
93 * time source (e.g. `(long)detws_time_now()`); 0 skips the time checks.
94 */
95bool jwt_bearer_valid_at(const char *auth_header, const uint8_t *secret, size_t secret_len, long now_epoch,
96 long leeway_s);
97
98/**
99 * @brief Read an integer claim (e.g. "exp", "iat", "nbf") from a JWT payload.
100 *
101 * base64url-decodes the payload segment and scans the JSON for a top-level
102 * numeric member @p name. Does not verify the signature - call
103 * jwt_verify_hs256() first.
104 *
105 * @param token the compact JWT string.
106 * @param token_len length of @p token.
107 * @param name claim name (without quotes).
108 * @param out receives the parsed value on success.
109 * @return true if the claim is present and parses as an integer.
110 */
111bool jwt_claim_int(const char *token, size_t token_len, const char *name, long *out);
112
113/**
114 * @brief Read a string claim (e.g. "sub", "role", "scope") from a JWT payload.
115 *
116 * base64url-decodes the payload and copies the top-level string member @p name
117 * into @p out (null-terminated, bounded by @p out_cap). Does not verify the
118 * signature - call jwt_verify_hs256() first. Minimal unescaping (handles `\"` and
119 * `\\`; other escapes are copied without their backslash), which suits
120 * scope / role / sub values.
121 *
122 * @return true if the claim is present and is a string that fit in @p out.
123 */
124bool jwt_claim_str(const char *token, size_t token_len, const char *name, char *out, size_t out_cap);
125
126/**
127 * @brief Test whether a space-separated OAuth2 scope claim grants @p required.
128 *
129 * @param scope_claim the `scope` claim value (space-delimited scopes, RFC 6749 3.3).
130 * @param required the scope to look for.
131 * @return true if @p required is one of the whole space-separated tokens.
132 */
133bool jwt_scope_allows(const char *scope_claim, const char *required);
134
135#endif // DETWS_ENABLE_JWT
136
137#endif // DETERMINISTICESPASYNCWEBSERVER_JWT_H
User-facing configuration for DeterministicESPAsyncWebServer.