OAuth 2.0 authorization and token exchange
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Reject empty access_token in parse_token_response

An empty string is not a usable bearer token. Now returns
Missing_access_token instead of Ok, catching the problem before
it surfaces as a failed userinfo request with "Bearer ".

+43 -12
+12 -1
lib/oauth.ml
··· 46 46 | Github -> "github" 47 47 | Google -> "google" 48 48 | Gitlab -> "gitlab" 49 + | Custom c -> c.name 50 + 51 + let provider_slug = function 52 + | Github -> "github" 53 + | Google -> "google" 54 + | Gitlab -> "gitlab" 49 55 | Custom c -> path_safe c.name 50 56 51 57 let authorize_url = function ··· 178 184 179 185 let parse_token_response body = 180 186 match decode token_response_jsont body with 181 - | Ok t -> Ok t 187 + | Ok t -> 188 + if t.access_token = "" then begin 189 + Log.warn (fun m -> m "Token parse failed: empty access_token"); 190 + Error Missing_access_token 191 + end 192 + else Ok t 182 193 | Error e -> 183 194 let err = classify_token_error body e in 184 195 Log.warn (fun m -> m "Token parse failed: %a" pp_parse_token_error err);
+31 -11
test/test_regressions.ml
··· 99 99 "missing access_token" (Error Oauth.Missing_access_token) 100 100 (Oauth.parse_token_response body) 101 101 102 + let test_parse_token_response_empty_access_token () = 103 + let body = {|{"access_token":""}|} in 104 + Alcotest.(check (result reject parse_token_error)) 105 + "empty access_token" (Error Oauth.Missing_access_token) 106 + (Oauth.parse_token_response body) 107 + 102 108 let test_parse_token_response_invalid_format () = 103 109 let body = {|{"access_token":12345}|} in 104 110 Alcotest.(check (result reject parse_token_error)) ··· 115 121 uid_field = "id"; 116 122 } 117 123 118 - let test_provider_name_path_safe () = 124 + let test_provider_name_is_raw () = 125 + (* provider_name returns the raw name for DB identity *) 119 126 Alcotest.(check string) "builtin" "github" (Oauth.provider_name Oauth.Github); 120 127 Alcotest.(check string) 121 - "slash" "corp-sso" 128 + "raw name" "corp/sso" 122 129 (Oauth.provider_name (custom "corp/sso")); 123 130 Alcotest.(check string) 124 - "spaces" "acme-sso" 131 + "preserves case" "Acme SSO" 125 132 (Oauth.provider_name (custom "Acme SSO")); 126 133 Alcotest.(check string) 134 + "unicode preserved" "\xe4\xbc\x81\xe6\xa5\xad" 135 + (Oauth.provider_name (custom "\xe4\xbc\x81\xe6\xa5\xad")) 136 + 137 + let test_provider_slug_is_path_safe () = 138 + (* provider_slug returns a URL-safe slug for routes *) 139 + Alcotest.(check string) "builtin" "github" (Oauth.provider_slug Oauth.Github); 140 + Alcotest.(check string) 141 + "slash" "corp-sso" 142 + (Oauth.provider_slug (custom "corp/sso")); 143 + Alcotest.(check string) 144 + "spaces" "acme-sso" 145 + (Oauth.provider_slug (custom "Acme SSO")); 146 + Alcotest.(check string) 127 147 "already clean" "myidp" 128 - (Oauth.provider_name (custom "myidp")); 148 + (Oauth.provider_slug (custom "myidp")); 129 149 Alcotest.(check string) 130 150 "special chars" "my-cool-provider" 131 - (Oauth.provider_name (custom "My Cool_Provider!")); 151 + (Oauth.provider_slug (custom "My Cool_Provider!")); 132 152 Alcotest.(check string) 133 153 "non-ascii fallback" "custom" 134 - (Oauth.provider_name (custom "\xe4\xbc\x81\xe6\xa5\xad")); 135 - Alcotest.(check string) 136 - "mixed utf8" "sso" 137 - (Oauth.provider_name (custom "\xe4\xbc\x81\xe6\xa5\xadSSO")) 154 + (Oauth.provider_slug (custom "\xe4\xbc\x81\xe6\xa5\xad")) 138 155 139 156 let test_readme_uses_current_api_names () = 140 157 let readme = read_file [ "README.md"; "ocaml-oauth/README.md" ] in ··· 179 196 test_parse_token_response_invalid_json; 180 197 Alcotest.test_case "parse_token_response missing access_token" `Quick 181 198 test_parse_token_response_missing_access_token; 199 + Alcotest.test_case "parse_token_response empty access_token" `Quick 200 + test_parse_token_response_empty_access_token; 182 201 Alcotest.test_case "parse_token_response invalid format" `Quick 183 202 test_parse_token_response_invalid_format; 184 - Alcotest.test_case "provider_name is path-safe" `Quick 185 - test_provider_name_path_safe; 203 + Alcotest.test_case "provider_name is raw" `Quick test_provider_name_is_raw; 204 + Alcotest.test_case "provider_slug is path-safe" `Quick 205 + test_provider_slug_is_path_safe; 186 206 ] )