bearer_auth.nx source
↩ module page · 127 lines · 4433 B
1// bearer_auth.nx -- extract Bearer tokens from Authorization header.
2//
3// RFC 6750. The modern HTTP auth scheme for OAuth 2.0, JWT,
4// API keys, and general \"the client holds a secret and presents
5// it\" flows. Trivial algorithmically but worth isolating behind
6// a helper so callers don't re-invent the case-insensitive prefix
7// parse + whitespace handling every time.
8//
9// Authorization: Bearer eyJhbGciOi...
10// ^^^^^^ ^^^^^^^^^^^^^
11// scheme token (opaque to HTTP)
12//
13// The token format itself (JWT / opaque random / signed session)
14// is caller's concern -- we just peel the \"Bearer \" prefix.
15//
16// Invariants:
17// BA1 Scheme match is case-insensitive per RFC 7235 ยง2.1.
18// BA2 Token is returned as (offset, length) into the caller's
19// buffer -- zero-alloc.
20// BA3 Multiple whitespace tolerated between scheme and token
21// (RFC permits 1 or more SP between credentials parts).
22
23import "syscalls.nx"
24
25const BR_ERR_FORMAT: i64 = -1
26const BR_ERR_SHORT: i64 = -2
27
28// ASCII lowercase.
29func br_lower(b: i64) -> i64 {
30 if b >= 0x41 {
31 if b <= 0x5A { return b + 0x20 }
32 }
33 return b
34}
35
36// Match the literal \"bearer\" case-insensitively at offset.
37func br_is_bearer(buf: *u8, off: i64, n: i64) -> i64 {
38 if n < off + 6 { return 0 }
39 if br_lower(buf[off]) != 0x62 { return 0 } // 'b'
40 if br_lower(buf[off + 1]) != 0x65 { return 0 } // 'e'
41 if br_lower(buf[off + 2]) != 0x61 { return 0 } // 'a'
42 if br_lower(buf[off + 3]) != 0x72 { return 0 } // 'r'
43 if br_lower(buf[off + 4]) != 0x65 { return 0 } // 'e'
44 if br_lower(buf[off + 5]) != 0x72 { return 0 } // 'r'
45 return 1
46}
47
48// Extract the Bearer token from an Authorization header VALUE
49// (not the full header; caller strips \"Authorization: \" first).
50// Writes (offset, length) of the token into *tok_off and *tok_len.
51// Returns 0 on success, negative on failure.
52func bearer_auth_extract(hdr: *u8, n: i64,
53 tok_off: *i64, tok_len: *i64) -> i64 {
54 if n < 7 { return BR_ERR_SHORT }
55 if br_is_bearer(hdr, 0, n) != 1 { return BR_ERR_FORMAT }
56 // Must be followed by at least one space.
57 var i: i64 = 6
58 if hdr[i] != 0x20 { return BR_ERR_FORMAT }
59 // Skip run of spaces.
60 while i < n {
61 if hdr[i] != 0x20 { break }
62 i = i + 1
63 }
64 if i >= n { return BR_ERR_FORMAT }
65
66 // Token runs to end-of-input or first whitespace (RFC permits
67 // trailing whitespace / additional fields in some grammars
68 // but for Authorization they're not standard).
69 let start: i64 = i
70 while i < n {
71 if hdr[i] == 0x20 { break }
72 if hdr[i] == 0x09 { break }
73 if hdr[i] == 0x0D { break }
74 if hdr[i] == 0x0A { break }
75 i = i + 1
76 }
77 *tok_off = start
78 *tok_len = i - start
79 return 0
80}
81
82// Convenience: returns 1 if the header starts with \"Bearer\"
83// (case-insensitive) and has at least some token content.
84func bearer_auth_is_present(hdr: *u8, n: i64) -> i64 {
85 let off: *i64 = (sys_mmap(16)) as *i64
86 let len: *i64 = (sys_mmap(16)) as *i64
87 if bearer_auth_extract(hdr, n, off, len) != 0 { return 0 }
88 if *len == 0 { return 0 }
89 return 1
90}
91
92// Compile-only smoke.
93func main() -> i64 {
94 let off: *i64 = (sys_mmap(16)) as *i64
95 let len: *i64 = (sys_mmap(16)) as *i64
96
97 // Happy path.
98 let hdr: *u8 = "Bearer eyJhbGciOiJIUzI1NiJ9"
99 if bearer_auth_extract(hdr, 27, off, len) != 0 { return 1 }
100 if *off != 7 { return 2 }
101 if *len != 20 { return 3 }
102 if hdr[*off] != 0x65 { return 4 } // 'e'
103
104 // Lowercase scheme.
105 let hdr2: *u8 = "bearer abc"
106 if bearer_auth_extract(hdr2, 10, off, len) != 0 { return 5 }
107 if *off != 7 { return 6 }
108 if *len != 3 { return 7 }
109
110 // Multiple spaces between scheme and token.
111 let hdr3: *u8 = "Bearer xyz"
112 if bearer_auth_extract(hdr3, 12, off, len) != 0 { return 8 }
113 if *off != 9 { return 9 }
114 if *len != 3 { return 10 }
115
116 // Wrong scheme -> error.
117 let hdr4: *u8 = "Basic dXNlcjpwYXNz"
118 if bearer_auth_extract(hdr4, 18, off, len) != BR_ERR_FORMAT {
119 return 11
120 }
121
122 // Presence helper.
123 if bearer_auth_is_present(hdr, 27) != 1 { return 12 }
124 if bearer_auth_is_present(hdr4, 18) != 0 { return 13 }
125
126 return 0
127}