eleven / messenger Public

Clone
git clone https://amadan.net/eleven/messenger

Plain git — no account needed to clone.

Download

Download this file

1import XCTest
2@testable import LChatCore
3
4// 🤖 Pins the native OIDC-door contract against home's Go trampoline tests
5// (home/oidc_test.go, home/github_test.go): the callback fragment shapes the
6// server emits are reproduced here verbatim, so client parsing and server
7// emission can't drift apart silently.
8final class HomeOIDCAuthTests: XCTestCase {
9 private let base = URL(string: "https://home.example.com")!
10
11 // ---- authorize URLs -----------------------------------------------------
12
13 func testGoogleAuthorizeURL() throws {
14 let url = try XCTUnwrap(HomeOIDCAuth.authorizeURL(
15 .google, base: base, state: "ios.abc", nonce: "n0nce"))
16 XCTAssertEqual(url.host, "accounts.google.com")
17 XCTAssertEqual(url.path, "/o/oauth2/v2/auth")
18 let q = try queryDict(url)
19 XCTAssertEqual(q["client_id"], HomeOIDCAuth.googleClientID)
20 XCTAssertEqual(q["redirect_uri"], "https://home.example.com/oidc/return")
21 XCTAssertEqual(q["response_type"], "id_token")
22 XCTAssertEqual(q["response_mode"], "form_post")
23 XCTAssertEqual(q["scope"], "openid")
24 XCTAssertEqual(q["nonce"], "n0nce")
25 XCTAssertEqual(q["state"], "ios.abc")
26 }
27
28 func testMicrosoftAuthorizeURL() throws {
29 let url = try XCTUnwrap(HomeOIDCAuth.authorizeURL(
30 .microsoft, base: base, state: "mac.xyz", nonce: "n1"))
31 XCTAssertEqual(url.host, "login.microsoftonline.com")
32 XCTAssertEqual(url.path, "/common/oauth2/v2.0/authorize")
33 let q = try queryDict(url)
34 XCTAssertEqual(q["client_id"], HomeOIDCAuth.microsoftClientID)
35 XCTAssertEqual(q["state"], "mac.xyz")
36 }
37
38 func testGitHubAuthorizeURLNeedsClientID() throws {
39 XCTAssertNil(HomeOIDCAuth.authorizeURL(.github, base: base, state: "s", nonce: "n"))
40 XCTAssertNil(HomeOIDCAuth.authorizeURL(.github, base: base, state: "s", nonce: "n",
41 githubClientID: ""))
42 let url = try XCTUnwrap(HomeOIDCAuth.authorizeURL(
43 .github, base: base, state: "ios.s1", nonce: "ignored",
44 githubClientID: "Ov23liTESTID"))
45 XCTAssertEqual(url.host, "github.com")
46 let q = try queryDict(url)
47 XCTAssertEqual(q["client_id"], "Ov23liTESTID")
48 XCTAssertEqual(q["redirect_uri"], "https://home.example.com/oidc/return")
49 XCTAssertEqual(q["state"], "ios.s1")
50 // Code flow: no OIDC params ride along.
51 XCTAssertNil(q["nonce"])
52 XCTAssertNil(q["response_type"])
53 }
54
55 /// The web trampoline routes on this prefix — a native state must never
56 /// carry it, or the flow lands on the web page instead of the app.
57 func testNativeStatesNeverWebPrefixed() {
58 for state in ["ios." + HomeAnchorAuth.mintNonce(), "mac." + HomeAnchorAuth.mintNonce()] {
59 XCTAssertFalse(state.hasPrefix("web."))
60 }
61 }
62
63 // ---- callback parsing (fragments verbatim from the Go tests) ------------
64
65 func testCallbackParsesIDToken() throws {
66 // oidc_test.go: elevenmessenger://home-oidc#state=...&id_token=tok.en.value
67 let url = URL(string: "elevenmessenger://home-oidc#state=ios.abc&id_token=tok.en.value")!
68 let cb = try XCTUnwrap(HomeOIDCAuth.callback(url, state: "ios.abc"))
69 XCTAssertEqual(cb.idToken, "tok.en.value")
70 XCTAssertNil(cb.code)
71 }
72
73 func testCallbackParsesCode() throws {
74 // github_test.go: elevenmessenger://home-oidc#state=mac.r2&code=abc
75 let url = URL(string: "elevenmessenger://home-oidc#state=mac.r2&code=abc")!
76 let cb = try XCTUnwrap(HomeOIDCAuth.callback(url, state: "mac.r2"))
77 XCTAssertEqual(cb.code, "abc")
78 XCTAssertNil(cb.idToken)
79 }
80
81 func testCallbackRejectsWrongStateHostOrEmpty() {
82 let ok = URL(string: "elevenmessenger://home-oidc#state=ios.a&id_token=t")!
83 XCTAssertNil(HomeOIDCAuth.callback(ok, state: "ios.b"), "state must round-trip")
84 let wrongHost = URL(string: "elevenmessenger://home-siwa#state=ios.a&id_token=t")!
85 XCTAssertNil(HomeOIDCAuth.callback(wrongHost, state: "ios.a"))
86 let bare = URL(string: "elevenmessenger://home-oidc#state=ios.a")!
87 XCTAssertNil(HomeOIDCAuth.callback(bare, state: "ios.a"), "no credential → start over")
88 }
89
90 func testCallbackDecodesPercentEncoding() throws {
91 // The trampoline url.QueryEscapes both fields; percent-decoding must
92 // happen exactly once.
93 let url = URL(string: "elevenmessenger://home-oidc#state=ios.a&id_token=a%2Fb%3Dc")!
94 let cb = try XCTUnwrap(HomeOIDCAuth.callback(url, state: "ios.a"))
95 XCTAssertEqual(cb.idToken, "a/b=c")
96 }
97
98 private func queryDict(_ url: URL) throws -> [String: String] {
99 let items = try XCTUnwrap(URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems)
100 return Dictionary(uniqueKeysWithValues: items.map { ($0.name, $0.value ?? "") })
101 }
102}
103