A set of utilities for working with the AT Protocol in Elixir.

refactor: add opts argument to Oauth module

authored by lekkice.moe and committed by

Tangled c2c66aef fff4f183

+102 -24
+102 -24
lib/atex/oauth.ex
··· 52 52 Get a map cnotaining the client metadata information needed for an 53 53 authorization server to validate this client. 54 54 """ 55 - @spec create_client_metadata() :: map() 56 - def create_client_metadata() do 57 - key = Config.get_key() 55 + @type create_client_metadata_option :: 56 + {:key, JOSE.JWK.t()} 57 + | {:client_id, String.t()} 58 + | {:redirect_uri, String.t()} 59 + | {:extra_redirect_uris, list(String.t())} 60 + | {:scopes, String.t()} 61 + @spec create_client_metadata(list(create_client_metadata_option())) :: map() 62 + def create_client_metadata(opts \\ []) do 63 + opts = 64 + Keyword.validate!(opts, 65 + key: Config.get_key(), 66 + client_id: Config.client_id(), 67 + redirect_uri: Config.redirect_uri(), 68 + extra_redirect_uris: Config.extra_redirect_uris(), 69 + scopes: Config.scopes() 70 + ) 71 + 72 + key = Keyword.get(opts, :key) 73 + client_id = Keyword.get(opts, :client_id) 74 + redirect_uri = Keyword.get(opts, :redirect_uri) 75 + extra_redirect_uris = Keyword.get(opts, :extra_redirect_uris) 76 + scopes = Keyword.get(opts, :scopes) 77 + 58 78 {_, jwk} = key |> JOSE.JWK.to_public_map() 59 79 jwk = Map.merge(jwk, %{use: "sig", kid: key.fields["kid"]}) 60 80 61 81 %{ 62 - client_id: Config.client_id(), 63 - redirect_uris: [Config.redirect_uri() | Config.extra_redirect_uris()], 82 + client_id: client_id, 83 + redirect_uris: [redirect_uri | extra_redirect_uris], 64 84 application_type: "web", 65 85 grant_types: ["authorization_code", "refresh_token"], 66 - scope: Config.scopes(), 86 + scope: scopes, 67 87 response_type: ["code"], 68 88 token_endpoint_auth_method: "private_key_jwt", 69 89 token_endpoint_auth_signing_alg: "ES256", ··· 125 145 - `{:ok, :invalid_par_response}` - Server respondend incorrectly to the request 126 146 - `{:error, reason}` - Error creating authorization URL 127 147 """ 148 + @type create_authorization_url_option :: 149 + {:key, JOSE.JWK.t()} 150 + | {:client_id, String.t()} 151 + | {:redirect_uri, String.t()} 152 + | {:scopes, String.t()} 128 153 @spec create_authorization_url( 129 154 authorization_metadata(), 130 155 String.t(), 131 156 String.t(), 132 - String.t() 157 + String.t(), 158 + list(create_authorization_url_option()) 133 159 ) :: {:ok, String.t()} | {:error, any()} 134 160 def create_authorization_url( 135 161 authz_metadata, 136 162 state, 137 163 code_verifier, 138 - login_hint 164 + login_hint, 165 + opts \\ [] 139 166 ) do 167 + opts = 168 + Keyword.validate!(opts, 169 + key: Config.get_key(), 170 + client_id: Config.client_id(), 171 + redirect_uri: Config.redirect_uri(), 172 + scopes: Config.scopes() 173 + ) 174 + 175 + key = Keyword.get(opts, :key) 176 + client_id = Keyword.get(opts, :client_id) 177 + redirect_uri = Keyword.get(opts, :redirect_uri) 178 + scopes = Keyword.get(opts, :scopes) 179 + 140 180 code_challenge = :crypto.hash(:sha256, code_verifier) |> Base.url_encode64(padding: false) 141 - key = get_key() 142 181 143 182 client_assertion = 144 - create_client_assertion(key, Config.client_id(), authz_metadata.issuer) 183 + create_client_assertion(key, client_id, authz_metadata.issuer) 145 184 146 185 body = 147 186 %{ 148 187 response_type: "code", 149 - client_id: Config.client_id(), 150 - redirect_uri: Config.redirect_uri(), 188 + client_id: client_id, 189 + redirect_uri: redirect_uri, 151 190 state: state, 152 191 code_challenge_method: "S256", 153 192 code_challenge: code_challenge, 154 - scope: Config.scopes(), 193 + scope: scopes, 155 194 client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", 156 195 client_assertion: client_assertion, 157 196 login_hint: login_hint ··· 160 199 case Req.post(authz_metadata.par_endpoint, form: body) do 161 200 {:ok, %{body: %{"request_uri" => request_uri}}} -> 162 201 query = 163 - %{client_id: Config.client_id(), request_uri: request_uri} 202 + %{client_id: client_id, request_uri: request_uri} 164 203 |> URI.encode_query() 165 204 166 205 {:ok, "#{authz_metadata.authorization_endpoint}?#{query}"} ··· 192 231 - `{:ok, tokens, nonce}` - Successfully obtained tokens with returned DPoP nonce 193 232 - `{:error, reason}` - Error exchanging code for tokens 194 233 """ 234 + @type validate_authorization_code_option :: 235 + {:key, JOSE.JWK.t()} 236 + | {:client_id, String.t()} 237 + | {:redirect_uri, String.t()} 238 + | {:scopes, String.t()} 195 239 @spec validate_authorization_code( 196 240 authorization_metadata(), 197 241 JOSE.JWK.t(), 198 242 String.t(), 199 - String.t() 243 + String.t(), 244 + list(validate_authorization_code_option()) 200 245 ) :: {:ok, tokens(), String.t()} | {:error, any()} 201 246 def validate_authorization_code( 202 247 authz_metadata, 203 248 dpop_key, 204 249 code, 205 - code_verifier 250 + code_verifier, 251 + opts \\ [] 206 252 ) do 207 - key = get_key() 253 + opts = 254 + Keyword.validate!(opts, 255 + key: get_key(), 256 + client_id: Config.client_id(), 257 + redirect_uri: Config.redirect_uri(), 258 + scopes: Config.scopes() 259 + ) 260 + 261 + key = Keyword.get(opts, :key) 262 + client_id = Keyword.get(opts, :client_id) 263 + redirect_uri = Keyword.get(opts, :redirect_uri) 208 264 209 265 client_assertion = 210 - create_client_assertion(key, Config.client_id(), authz_metadata.issuer) 266 + create_client_assertion(key, client_id, authz_metadata.issuer) 211 267 212 268 body = 213 269 %{ 214 270 grant_type: "authorization_code", 215 - client_id: Config.client_id(), 216 - redirect_uri: Config.redirect_uri(), 271 + client_id: client_id, 272 + redirect_uri: redirect_uri, 217 273 code: code, 218 274 code_verifier: code_verifier, 219 275 client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", ··· 245 301 end 246 302 end 247 303 248 - def refresh_token(refresh_token, dpop_key, issuer, token_endpoint) do 249 - key = get_key() 304 + @type refresh_token_option :: 305 + {:key, JOSE.JWK.t()} 306 + | {:client_id, String.t()} 307 + | {:redirect_uri, String.t()} 308 + | {:scopes, String.t()} 309 + @spec refresh_token( 310 + String.t(), 311 + JOSE.JWK.t(), 312 + String.t(), 313 + String.t(), 314 + list(refresh_token_option()) 315 + ) :: 316 + {:ok, tokens(), String.t()} | {:error, any()} 317 + def refresh_token(refresh_token, dpop_key, issuer, token_endpoint, opts \\ []) do 318 + opts = 319 + Keyword.validate!(opts, 320 + key: get_key(), 321 + client_id: Config.client_id(), 322 + redirect_uri: Config.redirect_uri(), 323 + scopes: Config.scopes() 324 + ) 325 + 326 + key = Keyword.get(opts, :key) 327 + client_id = Keyword.get(opts, :client_id) 250 328 251 329 client_assertion = 252 - create_client_assertion(key, Config.client_id(), issuer) 330 + create_client_assertion(key, client_id, issuer) 253 331 254 332 body = %{ 255 333 grant_type: "refresh_token", 256 334 refresh_token: refresh_token, 257 - client_id: Config.client_id(), 335 + client_id: client_id, 258 336 client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", 259 337 client_assertion: client_assertion 260 338 }