1
+ import { describe , test , expect } from "bun:test" ;
2
+ import { isValidRedirectUri , parseRedirectUris } from "./oauth-validation" ;
3
+
4
+ describe ( "OAuth Validation" , ( ) => {
5
+ describe ( "parseRedirectUris" , ( ) => {
6
+ test ( "parses comma-separated URIs" , ( ) => {
7
+ const result = parseRedirectUris ( "https://app1.com,https://app2.com, https://app3.com " ) ;
8
+ expect ( result ) . toEqual ( [
9
+ "https://app1.com" ,
10
+ "https://app2.com" ,
11
+ "https://app3.com"
12
+ ] ) ;
13
+ } ) ;
14
+
15
+ test ( "handles empty string" , ( ) => {
16
+ expect ( parseRedirectUris ( "" ) ) . toEqual ( [ ] ) ;
17
+ } ) ;
18
+
19
+ test ( "filters out empty values" , ( ) => {
20
+ const result = parseRedirectUris ( "https://app1.com,,https://app2.com," ) ;
21
+ expect ( result ) . toEqual ( [ "https://app1.com" , "https://app2.com" ] ) ;
22
+ } ) ;
23
+ } ) ;
24
+
25
+ describe ( "isValidRedirectUri" , ( ) => {
26
+ test ( "validates exact match" , ( ) => {
27
+ const authorizedUris = [ "https://app.example.com/callback" ] ;
28
+
29
+ expect ( isValidRedirectUri ( "https://app.example.com/callback" , authorizedUris ) ) . toBe ( true ) ;
30
+ expect ( isValidRedirectUri ( "https://app.example.com/other" , authorizedUris ) ) . toBe ( false ) ;
31
+ } ) ;
32
+
33
+ test ( "validates wildcard paths" , ( ) => {
34
+ const authorizedUris = [ "https://app.example.com/*" ] ;
35
+
36
+ expect ( isValidRedirectUri ( "https://app.example.com/" , authorizedUris ) ) . toBe ( true ) ;
37
+ expect ( isValidRedirectUri ( "https://app.example.com/callback" , authorizedUris ) ) . toBe ( true ) ;
38
+ expect ( isValidRedirectUri ( "https://app.example.com/deep/path" , authorizedUris ) ) . toBe ( true ) ;
39
+
40
+ // Different domain should fail
41
+ expect ( isValidRedirectUri ( "https://evil.com/callback" , authorizedUris ) ) . toBe ( false ) ;
42
+ } ) ;
43
+
44
+ test ( "validates protocol" , ( ) => {
45
+ const authorizedUris = [ "https://app.example.com/callback" ] ;
46
+
47
+ // HTTP instead of HTTPS should fail
48
+ expect ( isValidRedirectUri ( "http://app.example.com/callback" , authorizedUris ) ) . toBe ( false ) ;
49
+ } ) ;
50
+
51
+ test ( "validates host and port" , ( ) => {
52
+ const authorizedUris = [ "https://app.example.com:3000/callback" ] ;
53
+
54
+ // Different port should fail
55
+ expect ( isValidRedirectUri ( "https://app.example.com/callback" , authorizedUris ) ) . toBe ( false ) ;
56
+ expect ( isValidRedirectUri ( "https://app.example.com:3000/callback" , authorizedUris ) ) . toBe ( true ) ;
57
+ expect ( isValidRedirectUri ( "https://app.example.com:4000/callback" , authorizedUris ) ) . toBe ( false ) ;
58
+ } ) ;
59
+
60
+ test ( "handles invalid URIs" , ( ) => {
61
+ const authorizedUris = [ "not-a-valid-uri" , "https://valid.com" ] ;
62
+
63
+ // Invalid redirect URI
64
+ expect ( isValidRedirectUri ( "not-a-valid-uri" , authorizedUris ) ) . toBe ( false ) ;
65
+
66
+ // Valid redirect URI with invalid authorized URI should still work if it matches valid one
67
+ expect ( isValidRedirectUri ( "https://valid.com" , authorizedUris ) ) . toBe ( true ) ;
68
+ } ) ;
69
+
70
+ test ( "handles empty inputs" , ( ) => {
71
+ expect ( isValidRedirectUri ( "" , [ "https://app.com" ] ) ) . toBe ( false ) ;
72
+ expect ( isValidRedirectUri ( "https://app.com" , [ ] ) ) . toBe ( false ) ;
73
+ } ) ;
74
+
75
+ test ( "prevents open redirect attacks" , ( ) => {
76
+ const authorizedUris = [ "https://app.example.com/callback" ] ;
77
+
78
+ // Various attack vectors
79
+ expect ( isValidRedirectUri ( "https://app.example.com.evil.com/callback" , authorizedUris ) ) . toBe ( false ) ;
80
+ expect ( isValidRedirectUri ( "https://[email protected] /callback" , authorizedUris ) ) . toBe ( false ) ;
81
+ expect ( isValidRedirectUri ( "//evil.com/callback" , authorizedUris ) ) . toBe ( false ) ;
82
+ expect ( isValidRedirectUri ( "https:evil.com/callback" , authorizedUris ) ) . toBe ( false ) ;
83
+ } ) ;
84
+ } ) ;
85
+ } ) ;
0 commit comments