An Elixir toolkit for the AT Protocol. hexdocs.pm/atex
elixir bluesky atproto decentralization

Add opts argument to functions in Atex.OAuth module #2

merged opened by lekkice.moe targeting main from lekkice.moe/atex: oauth-opts

This PR adds an opts argument to allow overriding config values. This makes integration with external frameworks easier, as secrets can be provided at the function call level.

Labels

None yet.

assignee

None yet.

Participants 2
AT URI
at://did:plc:dgzvruva4jbzqbta335jtvoz/sh.tangled.repo.pull/3mdgohawhku22
+102 -24
Diff #1
+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 }

History

2 rounds 5 comments
sign up or login to add to the discussion
1 commit
expand
refactor: add opts argument to Oauth module
expand 1 comment

Thank you!

pull request successfully merged
1 commit
expand
refactor: add opts argument to Oauth module
expand 4 comments

Permissions are a bit broken for my tangled repos at the moment, recreate this PR on https://github.com/cometsh/atex instead and I can merge there.

Nevermind, it's been resolved and I can do things now.

Could you use Keyword.validate & Keyword.get to be consistent with the rest of the codebase?

no problem, i fixed the typespecs too