nx_login_gate.nx source
↩ module page · 90 lines · 4270 B
1// nx_login_gate.nx -- ★NISHI LOGIN: the UNIFIED sovereign auth gate. Validates a request's credential in ANY
2// presentation -- `X-Nishi-Cap: <cap>` (API), `Authorization: Bearer <cap>` (OAuth/MCP), or `Authorization: Basic
3// <user:cap>` (git, curl, browsers) -- against the ocap capability system for a REQUIRED SCOPE. This is the shared
4// validator behind the edge `auth=` switch AND the git gate: one credential (a scoped, attenuable, revocable cap),
5// three wire forms. Composes capt_verify (nx_cap_token) + basic_auth_decode (nx_basic_auth) + b64_decode. The OPAQUE
6// session path (browser SSO) layers on top via nx_site_auth. license_tier: ORIGINAL
7import "nx_syscalls.nx"
8import "nx_cap_token.nx"
9import "nx_basic_auth.nx"
10
11// case-insensitive: does s[0..len] equal `lower` (already lowercase)?
12func lg_ci_eq(s: *u8, lower: *u8, len: i64) -> i64 {
13 var i: i64 = 0
14 while i < len {
15 var c: i64 = s[i] as i64
16 if c >= 65 { if c <= 90 { c = c + 32 } }
17 if c != (lower[i] as i64) { return 0 }
18 i = i + 1
19 }
20 return 1
21}
22
23// find header <name> (lowercase, no colon). On success write value offset+len (trimmed) and return 1; else 0.
24func lg_header(req: *u8, n: i64, name: *u8, namelen: i64, voff: *i64, vlen: *i64) -> i64 {
25 var i: i64 = 0
26 var found: i64 = 0
27 while i < n {
28 if found == 0 {
29 var linestart: i64 = 0
30 if i == 0 { linestart = 1 }
31 if i > 0 { if req[i - 1] == (10 as u8) { linestart = 1 } }
32 if linestart == 1 { if i + namelen + 1 <= n {
33 if lg_ci_eq((req as i64 + i) as *u8, name, namelen) == 1 { if req[i + namelen] == (58 as u8) {
34 var v: i64 = i + namelen + 1
35 var sk: i64 = 1
36 while sk == 1 { if v < n { if req[v] == (32 as u8) { v = v + 1 } else { sk = 0 } } else { sk = 0 } }
37 var e: i64 = v
38 var go: i64 = 1
39 while go == 1 { if e < n { let c: i64 = req[e] as i64; if c == 13 { go = 0 } else { if c == 10 { go = 0 } else { e = e + 1 } } } else { go = 0 } }
40 voff[0] = v; vlen[0] = e - v
41 found = 1
42 } }
43 } }
44 i = i + 1
45 } else { i = n }
46 }
47 return found
48}
49
50// extract the cap token from the request (X-Nishi-Cap | Authorization: Bearer | Authorization: Basic <user:cap>).
51// writes the token to `out` (cap bytes) and returns its length, or 0 if no credential present.
52func lg_extract_cap(req: *u8, n: i64, out: *u8, cap: i64) -> i64 {
53 let voff: *i64 = sys_mmap(16) as *i64
54 let vlen: *i64 = sys_mmap(16) as *i64
55 // 1) X-Nishi-Cap
56 if lg_header(req, n, "x-nishi-cap" as *u8, 11, voff, vlen) == 1 {
57 let l: i64 = vlen[0]
58 if l > 0 { if l <= cap { var i: i64 = 0; while i < l { out[i] = req[voff[0] + i]; i = i + 1 } return l } }
59 }
60 // 2) Authorization
61 if lg_header(req, n, "authorization" as *u8, 13, voff, vlen) == 1 {
62 let av: *u8 = (req as i64 + voff[0]) as *u8
63 let al: i64 = vlen[0]
64 // Bearer <cap>
65 if al > 7 { if lg_ci_eq(av, "bearer " as *u8, 7) == 1 {
66 let bl: i64 = al - 7
67 if bl <= cap { var i: i64 = 0; while i < bl { out[i] = av[7 + i]; i = i + 1 } return bl }
68 } }
69 // Basic <user:cap> -> cap is the password half
70 if al > 6 { if lg_ci_eq(av, "basic " as *u8, 6) == 1 {
71 let ub: *u8 = sys_mmap(256)
72 let ul: *i64 = sys_mmap(16) as *i64
73 let pl: *i64 = sys_mmap(16) as *i64
74 if basic_auth_decode(av, al, ub, 256, ul, out, cap, pl) == 0 { return pl[0] }
75 } }
76 }
77 return 0
78}
79
80// THE GATE: is the request authorized for `scope`? secret = cap signing key (tools_cap_secret.key). now = epoch.
81// returns 1 (authorized) / 0 (deny -- absent/invalid/expired/wrong-scope/revoked via capt_verify).
82func login_gate_cap(req: *u8, n: i64, secret: *u8, seclen: i64, scope: *u8, scopelen: i64, now: i64) -> i64 {
83 let tok: *u8 = sys_mmap(4096)
84 let tl: i64 = lg_extract_cap(req, n, tok, 4096)
85 if tl <= 0 { return 0 }
86 if capt_verify(secret, seclen, tok, tl, scope, scopelen, now) == 1 { return 1 }
87 return 0
88}
89
90func main() -> i64 { return 0 }