GithubHelp home page GithubHelp logo

pow-auth / assent Goto Github PK

View Code? Open in Web Editor NEW
352.0 8.0 41.0 568 KB

Multi-provider framework in Elixir

Home Page: https://powauth.com

License: MIT License

Elixir 100.00%
multi-provider elixir oauth2 oauth apple-sign-in sign-in-with-apple auth0 azure-active-directory basecamp discord

assent's People

Contributors

0x6a68 avatar danschultzer avatar dtluther avatar evnu avatar fabiosammy avatar gabrielrinaldi avatar hwuethrich avatar joetrimble avatar kianmeng avatar mbuhot avatar nayshins avatar ohmree avatar praveenperera avatar raygesualdo avatar schultzer avatar sjednac avatar sondr3 avatar wingyplus avatar yordis avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

assent's Issues

Should OIDC fetch_user, fetch_userinfo, and validate_id_token allow for dynamic OpenID configuration?

Hi, and thanks for building and maintaining this ๐Ÿ‘‹.

In the configuration documentation for OIDC, :openid_configuration isn't strictly required since it can be fetched from :openid_configuration_uri if it isn't defined. Similarly, :openid_configuration_uri is also optional, since it defaults to /.well-known/openid-configuration based on :site.

Both authorize_url/1 and callback/3 work this way by calling openid_configuration/1. However, fetch_user/2, fetch_userinfo/2, and validate_id_token/2 are using Config.fetch/2 to resolve configuration, so they aren't getting it dynamically.

Should these work consistently? I'm a bit new to both Elixir and OIDC, so it's quite possible my understanding is off here.

If the intent is to have them all get :openid_configuration dynamically, I'm happy to try submitting a PR.

Thanks for your time!

Slack always "asks" for permission

Hello!

When a user uses the "Sign in with Slack" functionality to authenticate, Slack always "asks" for permission. That confuses users. Usually the identity provider asks once and just redirects on subsequent sign ins.

Screenshot 2021-09-30 at 10 59 38

I've asked Slack Support about it and they said:

It is the case that the prior SIWS (Sign in With Slack) functionality (which used identity.* scopes) asks for permission each time users accesses your resources.

However, we have updated this to a flow that's based on the OpenID Connect standard, and uses the openid scope. You can read more about that on the following updated SIWS page:

https://api.slack.com/authentication/sign-in-with-slack

I just wanted to track it here first. Probably I'll have some time to dig deeper about that in Assent. Any feedback is welcome :)

Assent.Strategy.Apple callback verification fails when using Assent.JWTAdapter.JOSE

When using the Assent.JWTAdapter.JOSE for Assent.Strategy.Apple, the following error occurs:

** (FunctionClauseError) no function clause matching in Assent.JWTAdapter.JOSE.jwk/2    
    
    The following arguments were given to Assent.JWTAdapter.JOSE.jwk/2:
    
        # 1
        "RS256"
    
        # 2
        nil
    
    Attempted function clauses (showing 3 out of 3):
    
        defp jwk(<<"HS"::binary(), _rest::binary()>>, secret)
        defp jwk(_alg, key) when is_binary(key)
        defp jwk(_alg, key) when is_map(key)
    
    (assent) lib/assent/jwt_adapter/jose.ex:24: Assent.JWTAdapter.JOSE.jwk/2
    (assent) lib/assent/jwt_adapter/jose.ex:46: Assent.JWTAdapter.JOSE.verify/3
    (assent) lib/assent/strategies/apple.ex:108: Assent.Strategy.Apple.get_user/2
    (assent) lib/assent/strategies/oauth2.ex:237: Assent.Strategy.OAuth2.fetch_user/3
    (assent) lib/assent/strategies/oauth2/base.ex:69: Assent.Strategy.OAuth2.Base.callback/3

The error is caused by a nil parameter being passed for the public key in the following line:

with {:ok, jwt} <- Helpers.verify_jwt(token["id_token"], nil, config) do

I was able to work around the issue by providing the Apple Public Key from https://appleid.apple.com/auth/keys as a configuration parameter as follows:

  def get_user(config, token) do
    public_key = Config.get(config, :public_key, nil)
    with {:ok, jwt} <- Helpers.verify_jwt(token["id_token"], public_key, config) do
      {:ok, jwt.claims}
    end
  end

with the :public_key specified in my config as follows:

config :my_app, :pow_assent,
  providers: [
    apple: [
      strategy: Assent.Strategy.Apple,
      client_id: System.get_env("APPLE_CLIENT_ID"),
      team_id: System.get_env("APPLE_TEAM_ID"),
      private_key_id: System.get_env("APPLE_PRIVATE_KEY_ID"),
      private_key: System.get_env("APPLE_PRIVATE_KEY"),
      jwt_adapter: Assent.JWTAdapter.JOSE,
      public_key: %{
        "kty" => "RSA",
        "kid" => "AIDOPK1",
        "use" => "sig",
        "alg" => "RS256",
        "n" =>
          "lxrwmuYSAsTfn-lUu4goZSXBD9ackM9OJuwUVQHmbZo6GW4Fu_auUdN5zI7Y1dEDfgt7m7QXWbHuMD01HLnD4eRtY-RNwCWdjNfEaY_esUPY3OVMrNDI15Ns13xspWS3q-13kdGv9jHI28P87RvMpjz_JCpQ5IM44oSyRnYtVJO-320SB8E2Bw92pmrenbp67KRUzTEVfGU4-obP5RZ09OxvCr1io4KJvEOjDJuuoClF66AT72WymtoMdwzUmhINjR0XSqK6H0MdWsjw7ysyd_JhmqX5CAaT9Pgi0J8lU_pcl215oANqjy7Ob-VMhug9eGyxAWVfu_1u6QJKePlE-w",
        "e" => "AQAB"
      }
    ]
  ]

Hard coding the Apple public key is just a work around to get me up and running. I notice that the Ueberauth Apple strategy fetches the Apple public key during the verification phase and I guess the Assent Apple strategy should do the same?

Add connection pool to Mint HTTP adapter (turn it into a GenServer)

Based on pow-auth/pow_assent#89

So the current implementation of PowAssent.HTTPAdapter.Mint doesn't pool HTTP/2 connections. For it to do that, I'll need to set it up as a GenServer. This way all connections can be kept open and all requests can be faster and more efficient. As new requests are coming in, the connections will be created if they don't exist in the GenServer state.

I haven't decided yet if the GenServer should be an extra layer on top of the current implementation so they work in parallel, or it should just replace it entirely and you are required to start it up to use Mint. I feel the latter is the better choice, since there is not really much of a point in using Mint without connection pool.

Any feedback/thoughts are welcome, I only have a superficial understanding of HTTP/2 ๐Ÿ™‚

Bad Request - Invalid Header with Entra ID (Azure AD) and Finch

I am getting a 400 when sending requests to login.microsoftonline.com while using the Finch adapter.

%{
  name: InternalFinch,
  request: %Finch.Request{
    body: nil,
    headers: [{"User-Agent", "Assent-0.2.9"}],
    host: "login.microsoftonline.com",
    method: "GET",
    path: "/organizations/v2.0/.well-known/openid-configuration",
    port: 443,
    private: %{},
    query: nil,
    scheme: :https,
    unix_socket: nil
  },
  result: {:ok,
   %Finch.Response{
     body: "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\"http://www.w3.org/TR/html4/strict.dtd\">\r\n<HTML><HEAD><TITLE>Bad Request</TITLE>\r\n<META HTTP-EQUIV=\"Content-Type\" Content=\"text/html; charset=us-ascii\"></HEAD>\r\n<BODY><h2>Bad Request - Invalid Header</h2>\r\n<hr><p>HTTP Error 400. The request has an invalid header name.</p>\r\n</BODY></HTML>\r\n",
     headers: [
       {"content-type", "text/html; charset=us-ascii"},
       {"date", "Mon, 11 Mar 2024 15:55:17 GMT"},
       {"connection", "close"},
       {"content-length", "339"}
     ],
     status: 400
   }},
}

I'm not sure if Microsoft is flagging this User-Agent as a bot, or there's actually an issue with the payload, but here are the only headers that seem to be sent in the request:

    headers: [{"User-Agent", "Assent-0.2.9"}],

Do we have any control over the headers sent in the payload while using an http adapter? I'd like to try and debug if adding/removing this header can make a difference.

Here are my dependencies:

{:assent, "~> 0.2.9"},
{:finch, "~> 0.16"},

any help would be much appreciated!

Allow passing access token to callback

Right now assent assumes that the client will pass code in parameter list in the callback phase. There are cases, though, when the client has an access token already (assent does not need to fetch it and does not have the means to do so). Would it be possible and desirable to accept access tokens as well in OAuth2 strategy.

My specific use case is Facebook login on mobile devices where client side libraries only allow to fetch the access token straight away.

Warnings with Elixir 1.11

During compilation, I get the warnings below repeatedly. It seems to be because oauther needs to have :crypto and :public_key its :extra_applications config.

I raised it here: lexmag/oauther#18

I also notice it's not seen a commit in over 2.5 years, so I thought I would bring the point up over here, as well.

Thank you, Dan, for all the hard work & sharing!

MSIS9691: Received invalid OAuth request. The Basic Authorization header must be Base64 encoded.

I am trying to use PowAssent with OAuth2 strategy against an ADFS-provider and it's almost working, but even though I can see the access_token as a param in the request, something is not quite right yet. Probably I am just missing something relevant. I thought, I'd report just in case anyone else faces this (ungoogleable) error code in the subject.

Glad for any pointers, let me know, if you need more info.
Thank you and cheers!

P.S. I did this:
PPS: I realize now that this might belong into the Assent repo ๐Ÿค•

scope "/" do
    pipe_through :skip_csrf_protection

    post "/auth/:provider/callback", PowAssent.Phoenix.AuthorizationController, :callback
end

================================================
warning: This request will NOT be verified for valid SSL certificate
(assent) lib/assent/http_adapter/httpc.ex:22: Assent.HTTPAdapter.Httpc.request/5
(assent) lib/assent/strategy.ex:42: Assent.Strategy.request/5
(assent) lib/assent/strategies/oauth2.ex:222: Assent.Strategy.OAuth2.get_access_token/2
(assent) lib/assent/strategies/oauth2.ex:119: Assent.Strategy.OAuth2.callback/3
(assent) lib/assent/strategies/oauth2/base.ex:69: Assent.Strategy.OAuth2.Base.callback/3
(pow_assent) lib/pow_assent/plug.ex:74: PowAssent.Plug.callback/4
(pow_assent) lib/pow_assent/phoenix/controllers/authorization_controller.ex:39: PowAssent.Phoenix.AuthorizationController.process_callback/2
(pow) lib/pow/phoenix/controllers/controller.ex:99: Pow.Phoenix.Controller.action/3
(pow_assent) lib/pow_assent/phoenix/controllers/authorization_controller.ex:1: PowAssent.Phoenix.AuthorizationController.action/2
(pow_assent) lib/pow_assent/phoenix/controllers/authorization_controller.ex:1: PowAssent.Phoenix.AuthorizationController.phoenix_controller_pipeline/2
(phoenix) lib/phoenix/router.ex:288: Phoenix.Router.call/2
(my_app) lib/my_app_web/endpoint.ex:1: MyAppWeb.Endpoint.plug_builder_call/2
(my_app) lib/plug/debugger.ex:122: MyAppWeb.Endpoint."call (overridable 3)"/2
(my_app) lib/my_app_web/endpoint.ex:1: MyAppWeb.Endpoint.call/2
(phoenix) lib/phoenix/endpoint/cowboy2_handler.ex:42: Phoenix.Endpoint.Cowboy2Handler.init/4
(cowboy) c:/src/my_app/deps/cowboy/src/cowboy_handler.erl:41: :cowboy_handler.execute/2
(cowboy) c:/src/my_app/deps/cowboy/src/cowboy_stream_h.erl:320: :cowboy_stream_h.execute/3

[error] #PID<0.1578.0> running SmartpowerWeb.Endpoint (connection #PID<0.1549.0>, stream id 5) terminated

Server: localhost:4200 (http)
Request: POST /auth/oauth2/callback
** (exit) an exception was raised:
** (Assent.RequestError) Server responded with status: 400

Headers:
cache-control: no-store
date: Mon, 11 Nov 2019 21:27:16 GMT
pragma: no-cache
server: Microsoft-HTTPAPI/2.0 Microsoft-HTTPAPI/2.0
content-length: 146
content-type: application/json;charset=UTF-8

Body:
%{"error" => "invalid_request", "error_description" => "MSIS9691: Received invalid OAuth request. The Basic Authorization header must be Base64 encoded."}

         (pow_assent) lib/pow_assent/phoenix/controllers/authorization_controller.ex:190: PowAssent.Phoenix.AuthorizationController.handle_strategy_error/1
    (pow) lib/pow/phoenix/controllers/controller.ex:99: Pow.Phoenix.Controller.action/3
    (pow_assent) lib/pow_assent/phoenix/controllers/authorization_controller.ex:1: PowAssent.Phoenix.AuthorizationController.action/2
    (pow_assent) lib/pow_assent/phoenix/controllers/authorization_controller.ex:1: PowAssent.Phoenix.AuthorizationController.phoenix_controller_pipeline/2
    (phoenix) lib/phoenix/router.ex:288: Phoenix.Router.__call__/2
    (myapp) lib/myapp_web/endpoint.ex:1: MyAppWeb.Endpoint.plug_builder_call/2
    (myapp) lib/plug/debugger.ex:122: MyAppWeb.Endpoint."call (overridable 3)"/2
    (myapp) lib/myapp_web/endpoint.ex:1: MyAppWeb.Endpoint.call/2
    (phoenix) lib/phoenix/endpoint/cowboy2_handler.ex:42: Phoenix.Endpoint.Cowboy2Handler.init/4
    (cowboy) c:/src/myapp/deps/cowboy/src/cowboy_handler.erl:41: :cowboy_handler.execute/2
    (cowboy) c:/src/myapp/deps/cowboy/src/cowboy_stream_h.erl:320: :cowboy_stream_h.execute/3
    (cowboy) c:/src/myapp/deps/cowboy/src/cowboy_stream_h.erl:302: :cowboy_stream_h.request_process/3
    (stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3

Auth0: Support Variable-Length Access Token and Authorization Codes

Hello Dan & Pow crew!

I've found tremendous value in this library and I've just realized I should be pushing for us to sponsor the project!

Auth0 has notified me about a deprecation in their API for fixed length access tokens and authorization codes. They're apparently doing a hard cut-over on April 12th. At that point, I expect things will stop working properly as my Auth0 log says the following when I log in:

Fixed Length of Access Token & Authorization Code: This feature is being deprecated. Please see https://auth0.com/docs/product-lifecycle/deprecations-and-migrations#opaque-access-token-for-userinfo for more information.

What's the best course of action to make sure I remain compatible? I'm going to need to dig in deeper, but this is a bit foreign to me. I could certainly use a hand!

And thank you so much for the time & energy put into the pow/assent ecosystem.

Adam

Azure AD should expose more user info

I'm currently struggling to find a way how to map additional claims from Azure AD provider into user_identity changeset.

These are the claims that Azure AD returns:

claims: %{
    "aud" => "93572ac3-0740-4aa5-ad35-18ba25d5fe22",
    "email" => "[email protected]",
    "exp" => 1642465568,
    "iat" => 1642461668,
    "iss" => "https://login.microsoftonline.com/6ee623a2-0b05-4ea4-b931-79c555955cb1/v2.0",
    "name" => "Moravec Albert",
    "nbf" => 1642461668,
    "oid" => "09cbdc15-ccf1-43e9-a2fb-8e9d9513d5cc",
    "preferred_username" => "[email protected]",
    "rh" => "0.AToAoiPmbgULpE65MXnFVZVcscMqV5NAB6VKrTUYuiXV_iI6AO4.",
    "roles" => ["administrator", "manager"],
    "sub" => "1ct7-HTE7-CM5h5H7009_9lRRLdHiAHt1hY30ogqji0",
    "tid" => "6ee623a2-0b05-4ea4-b931-79c555955cb1",
    "uti" => "5aLPSh0CRUW_doLHsZxjAA",
    "ver" => "2.0"
}

Since standard OpenID Connect mapping is used, all of the claims except the standardized ones are thrown away. Is there a way to override this behavior?
I personally expected this to be doable at least on the Strategy level by customizing the normalize/2, but the user there is already stripped of all the claims.

Sign in with Apple: Strategy failed with error: :enoent

So i'm trying to implement sign in with apple using pow_assent, but after signing in, it just returns an error flash Something went wrong, and you couldn't be signed in. Please try again

When I looked at the logs it shows:

Screenshot 2023-05-30 at 5 32 24 PM

Do you have any idea what causes and how to fix this? Thanks!

AssentJWT adapter returns unexpected value

The possible return values of Assent.JWTAdapter.AssentJWT.verify/3 do not match the callback definition of Assent.JWTAdapter.verify/3 ๐Ÿค”

Here is the callback definition:

@callback verify(binary(), binary() | map() | nil, Keyword.t()) :: {:ok, map()} | {:error, any()}

Calling Assent.JWTAdapter.AssentJWT.verify/2 can also return just :error ๐Ÿ˜ฒ Have a look into the example below:

token = "eyJhbGciOiJSUzI1NiIsImtpZCI6ImV4YW1wbGUiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhc3NlbnQtaXNzdWUiLCJleHAiOjE3MTk0ODk5MDcsInN1YiI6IjQyIn0.o3Lijaa_Q5m1Tr7A6myopsdtO771SlVu9rK-ElIqdYcj5eo5PochP3cqG7NFw76TqyRMdNwkr-OQevAWUbcireZ-dSKtkhtYm91M2sVM_RI5s7LTfdbUTHEleCAg_x0pC1mkDRmKSKBPuooEKypIl6P6ME0GogC_4SthzYRx3gks6m9TNI8wCzRhOaGuLEvnOm8KoQXpreVLfibe_C_v30AiJVzxMoqskQKJ9hagE34I9SB6ut3_ZsczMisjXe5cGLcsWSctAoi-JaIqUk3pMyfgUVVvt8ZzbV2LvqPz2O1vOAT-QDui68szYFAJh9IaNbwVC9QdLmthdg01DLYrA"

secret = %{
  "alg" => "RS256",
  "e" => "AQAB",
  "kid" => "example",
  "kty" => "RSA",
  "n" => "g5IE_tFgft5wRYPwivPY4QpNc6IpbZv5w24tW2rKdS74ntwhPQo38yahAOUujTAbpUvN3motDtJOkjov9O6fxhFkjOEA4zxXFGxHWyMwloRjNen9uScbi88EuVaSuTWKoq4C9FE650222QrtU0SImMSgi166sbTbi5bvS9hanSphksmw8vdVff56aFE5jOpVXNEoFoj3CFTRfesIhht9qXJp-HFbbSkDcyeQ1Y5rv5nzKQXMONTxxhV-qaJ03BNLxFVOP9eqeVTUHQoQ262qpbz-3qRWxxqy5Q2V0g4yk-IJPV2u6yNd5SojsRK3V5YIC3b0_VuDXN_we0o-sOjESQ",
  "use" => "sig"
}

Assent.JWTAdapter.AssentJWT.verify(id_token, secret, [json_library: Jason])

The :error is caused by a broken token I passed in. When using a valid token (see below), you get an {:ok, _} tuple.

eyJhbGciOiJSUzI1NiIsImtpZCI6ImV4YW1wbGUiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhc3NlbnQtaXNzdWUiLCJleHAiOjE3MTk0ODk5MDcsInN1YiI6IjQyIn0.Zo3Lijaa_Q5m1Tr7A6myopsdtO771SlVu9rK-ElIqdYcj5eo5PochP3cqG7NFw76TqyRMdNwkr-OQevAWUbcireZ-dSKtkhtYm91M2sVM_RI5s7LTfdbUTHEleCAg_x0pC1mkDRmKSKBPuooEKypIl6P6ME0GogC_4SthzYRx3gks6m9TNI8wCzRhOaGuLEvnOm8KoQXpreVLfibe_C_v30AiJVzxMoqskQKJ9hagE34I9SB6ut3_ZsczMisjXe5cGLcsWSctAoi-JaIqUk3pMyfgUVVvt8ZzbV2LvqPz2O1vOAT-QDui68szYFAJh9IaNbwVC9QdLmthdg01DLYrA

Boolean field may contain an error-tuple which can cause issues

The map returned by Assent.JWTAdapter.AssentJWT.verify/3 may have the verified?-key set to an {:error, _} tuple, which may cause issues when doing a simple check for trueness.

token = "..."
secret = %{...}

{:ok, details} = Assent.JWTAdapter.AssentJWT.verify(token, secret, [json_adapter: Jason])

if details.verified? do
  IO.puts("Everything is cool!")
else
  IO.puts("Verification failed.")
end

You may see Everthing is cool! although an error occurred, because verified? contains an {:error, _} tuple. Here's an example of where the tuple can come from:

defp decode_key(jwk) when is_map(jwk), do: {:error, "Can't decode JWK"}

Sadly, I cannot provide you with the token and secret I stumpled upon that error, but by looking at the code, you already see that the error-tuple is a possible value for the verified?-field.

Clarify apple usage

Hi guys,

While implementing the endboss, aka sign in with Apple I ran into some issues.

The docs in Assent.Strategy.Apple specifically talk about the JS SDK, but I couldn't figure out how to get the state to be passed
in and read back correctly.

In the end I resorted to just using the Routes.pow_assent_authorization_url/3, which worked like a charm.

Long story short, it would be great to give people a pointer to just using the regular /auth/apple/new flow, especially since the apple docs only talk about their JS SDK as well.

JWT logic

There is very weak JWT parsing in the current library. It would probably be best if there actually is some validation done on the JWT instead. It should either be a built-in module, or a third-party module that can be changed like with :http_adapter.

Multiple audiences

Hello! I'm pretty new to Elixir so forgive me if this has an easy answer. I'm creating a custom OIDC provider (Netsuite) that issues 2 audiences in a list, one of which is the client ID.

In the oidc.ex, audience validation simply matches what's returned in the claim with the client_id but unfortunately this always produces the "Invalid audience" error.

How would I go about filtering the data so that I could either remove the other audience or add it to what's being matched? I'm trying to follow along with the Apple OIDC provider but can't quite make sense of it.

OIDC normalize/2 to conform to OAuth2 normalize/2

I realize that the typespec for normalize/2 belonging to the OIDC strategy is different than the one described in the docs, and the OAuth2 one, and it is making my dialyzer fail.

I believe it should be changed to

@callback normalize(Keyword.t(), map()) :: {:ok, map()} | {:ok, map(), map()} | {:error, term()}

to allow claims that don't conform to the OIDC spec

Google Sign in for mobile strategy

Currently, assent has support for Google Sign in for the web, the client sends the code and scope and the strategy retrieves from google the JWT with the data.

This works fine for the web, but not so much for mobile apps where normally the client will retrieve the JWT (id token) directly and the server job is to validate it.

Since I need exactly this case, I created a new strategy for it, you can add it to Assent if you want, and also I would appreciate it if you can take a look and see if there is some flaw in the logic.

The strategy itself is very simple, you simply send the id_token to id and it will validate it.

defmodule Core.Identity.GoogleSignIn.Strategy do
  @moduledoc false

  alias Core.Identity.GoogleSignIn.JWTManager

  @behaviour Assent.Strategy

  @impl true
  def callback(_, params) do
    %{id_token: id_token} = params

    case JWTManager.verify_and_validate(id_token) do
      {:ok, user} -> {:ok, %{user: user}}
      {:error, _} -> {:error, :invalid_token}
    end
  end

  @impl true
  def authorize_url(_), do: throw("not implemented")
end

For validation, here are my modules:

defmodule Core.Identity.GoogleSignIn.JWTVerifyHook do
  @moduledoc false

  use Joken.Hooks

  @impl true
  def before_verify(_, {jwt, %Joken.Signer{}}) do
    with {:ok, %{"kid" => kid}} <- Joken.peek_header(jwt),
         {:ok, algorithm, key} <- GoogleCerts.fetch(kid) do
      {:cont, {jwt, Joken.Signer.create(algorithm, key)}}
    else
      _ -> {:halt, {:error, :no_signer}}
    end
  end
end
defmodule Core.Identity.GoogleSignIn.JWTManager do
  @moduledoc false

  alias Core.Identity.GoogleSignIn.JWTVerifyHook

  use Joken.Config, default_signer: nil

  add_hook(JWTVerifyHook)

  @iss "https://accounts.google.com"

  @impl true
  def token_config do
    default_claims(skip: [:aud, :iss])
    |> add_claim("iss", nil, fn iss -> iss == @iss end)
    |> add_claim("aud", nil, fn aud -> aud == aud() end)
  end

  defp aud do
    Application.fetch_env!(:core, :pow_assent)
       |> Keyword.fetch!(:providers)
       |> Keyword.fetch!(:google_sign_in)
       |> Keyword.fetch!(:client_id)
  end
end

Reliance on inets?

Hi,

Using assent to authenticate through oauth2 on a micro-app that had no ecto backup nor need to record user information past authorization, I encountered an error after the built succeed:

web_1  | ** (exit) an exception was raised:
web_1  |     ** (UndefinedFunctionError) function :httpc.request/4 is undefined (module :httpc is not available)
web_1  |         :httpc.request(:post, {'https://api.intra.42.fr/oauth/token', [{'User-Agent', 'Assent-0.1.16'}], 'application/x-www-form-urlencoded', 'code=d44d4959376eb30953852cc66cb4ebce50c4018dfda2dc79ebd26414bcac3ab2&redirect_uri=http%3A%2F%2Flocalhost%3A4000%2Fsession%2Fcallback&grant_type=authorization_code&client_id=54d568770a5f2984fdacf76bcf38d469edbe71b55ccd050dd83a63fd94f0ac6b&client_secret=b237063058109ba31701030463a9a77616fecab6b866e53f1b50e1c03da9d3e5'}, [], [])
web_1  |         (assent) lib/assent/http_adapter/httpc.ex:25: Assent.HTTPAdapter.Httpc.request/5
web_1  |         (assent) lib/assent/strategy.ex:42: Assent.Strategy.request/5
web_1  |         (assent) lib/assent/strategies/oauth2.ex:250: Assent.Strategy.OAuth2.grant_access_token/3
web_1  |         (assent) lib/assent/strategies/oauth2.ex:129: Assent.Strategy.OAuth2.callback/3
# ... here be where the binding was used in my app

That happened during the callback of the oauth2 transaction, and completely baffled me. It looked like something like a missing dependency or something, yet I did not see anything like this in assent's docs, and when I googled it, it looked exactly like the inets lib was missing...

I finally figured out how to fix this, adding inets to the extra_applications:

# in app/mix.exs
  def application do
    [
      # ...
      extra_applications: [
        # ...
        :inets
      ]
    ]
  end

...aaaand... that worked.

Maybe it should be noted in the installation docs that inets is necessary to be started for it to work. I'm willing to make a PR explaining it, but I'd like for someone knowing assent from the inside out to explain why and in which cases it's necessary to have inets as a dependency first (so I don't mislead others either).

So, there. Thank you for that awesome lib by the way.

support code_verifier/code_challenge in OAuth2

It is becoming an OAuth recommendation that state and code_verifier/code_challenge be used together to ensure that nobody can hijack your OAuth exchange code in server flows.

This simply allows you to pass your verifier to the oauth2 callback (it was literally impossible before) in case you need to. It is up to the user to generate your challenge and encode it.

This is an MVP for my use case so I am opening an issue and immediately opening a pull request for it.

Error with multitenant Azure login

[error] #PID<0.888.0> running TaskAppWeb.Endpoint (connection #PID<0.828.0>, stream id 27) terminated
Server: localhost:4006 (https)
Request: POST /auth/azure/callback
** (exit) an exception was raised:
** (RuntimeError) Invalid issuer "https://login.microsoftonline.com/270a4662-e407-4044-b299-1a62945d3893/v2.0" in ID Token
(pow_assent 0.4.6) lib/pow_assent/phoenix/controllers/authorization_controller.ex:209: PowAssent.Phoenix.AuthorizationController.handle_strategy_error/1
(pow_assent 0.4.6) lib/pow_assent/phoenix/controllers/authorization_controller.ex:1: PowAssent.Phoenix.AuthorizationController.action/2
(pow_assent 0.4.6) lib/pow_assent/phoenix/controllers/authorization_controller.ex:1: PowAssent.Phoenix.AuthorizationController.phoenix_controller_pipeline/2
(phoenix 1.4.16) lib/phoenix/router.ex:288: Phoenix.Router.call/2
(task_app 0.1.0) lib/task_app_web/endpoint.ex:1: TaskAppWeb.Endpoint.plug_builder_call/2
(task_app 0.1.0) lib/plug/debugger.ex:132: TaskAppWeb.Endpoint."call (overridable 3)"/2
(task_app 0.1.0) lib/task_app_web/endpoint.ex:1: TaskAppWeb.Endpoint.call/2
(phoenix 1.4.16) lib/phoenix/endpoint/cowboy2_handler.ex:42: Phoenix.Endpoint.Cowboy2Handler.init/4

Using Tesla for HTTP

Hey, first of all, thanks for the library, really good, but I have one question:

Do you think it could make sense to use Tesla as the HTTP abstraction so the library could use any of the Tesla adapters out there? Or we could have a an Assent.HTTPAdapter for Tesla, but then we would have adapter of the adapter xD.

Tesla currently have adapters for Finch, Gun, hackney, httpc, ibrowse and Mint.

Tesla also have a test adapter which might come in handy.

https://hexdocs.pm/tesla/Tesla.Adapter.html#content

Yandex OAuth Strategy

Hello there, would you mind if I added strategy for Yandex OAuth? It might be useful for someone who uses that provider.

How to get the authorization code on react native for the Facebook strategy

I'm sorry, I know that this is not really directly related to assent.

The Facebook strategy requires an authorization code for the callback.

In this issue you say that "The Facebook strategy docs now highlights how to fetch the code client side to submit server side".

#34 (comment)

Our problem is that we are using https://github.com/facebook/react-native-fbsdk to implement the frontend facebook auth in react native. So we I don't think we can use the JS sdk to get the signed request and get the authorization code.

Please could you point me in the right direction on how to get the code from the accessToken in this situation?

Assent.Strategy.Apple doesn't work with Assent.JWTAdapter.AssentJWT

The client_secret generated by Assent.JWTAdapter.AssentJWT doesn't have a valid signature and returns an "invalid_client" error when handling the callback from Apple. I confirmed that the signature was not valid by decoding the client secret at https://jwt.io/ and providing the public key for my Apple generated private key.

In case this helps, I used the following command to generate a public key from the private key:

openssl ec -in AuthKey_xxxxxxxxx.p8 -pubout -out public.pem

Switching to Assent.JWTAdapter.JOSE adapter does generate the correct client secret.

Callback param "user" is not parsed when using Apple strategy with "name" scope

When using the "name" scope with sign-in with Apple, it doesn't seem like the "user" map that contains "firstName" and "lastName" is decoded, or added to the token map in OAuth2.callback/3. This means that when Strategy.Apple attempts to merge the name params into the other user params, nothing is there. In my application this results in "given_name" and "family_name" being absent when trying to insert a new user into the database.

I was able to fix this in my project by decoding the "user" map in OAuth2.callback/3, and assigning it to token before passing it off to fetch_user_with_strategy as follows:

  @impl true
  @spec callback(Config.t(), map(), atom()) :: {:ok, %{user: map(), token: map()}} | {:error, term()}
  def callback(config, params, strategy \\ __MODULE__) do
    with {:ok, session_params} <- Config.fetch(config, :session_params),
         :ok                   <- check_error_params(params),
         {:ok, code}           <- fetch_code_param(params),
         {:ok, redirect_uri}   <- Config.fetch(config, :redirect_uri),
         :ok                   <- maybe_check_state(session_params, params),
-        {:ok, token}          <- grant_access_token(config, "authorization_code", code: code, redirect_uri: redirect_uri) do
+        {:ok, token}          <- grant_access_token(config, "authorization_code", code: code, redirect_uri: redirect_uri),
+        token                 <- maybe_add_user_callback_params(config, token, params) do

      fetch_user_with_strategy(config, token, strategy)
    end
  end

+ defp maybe_add_user_callback_params(config, token, %{"provider" => "apple", "user" => user}) do
+   Map.put(token, "user", Config.json_library(config).decode!(user))
+ end
+ defp maybe_add_user_callback_params(_config, token, _params), do: token

Does this seem right? I've looked pretty closely at the code paths involved, and don't see any other place where this information seems to be handled. Happy to open a PR if this is a real issue, and if the fix seems appropriate!

OTP 24 :crypto.hmac/3 doesn't exist anymore

warning: :crypto.hmac/3 is undefined or private. Did you mean one of:

      * mac/3
      * mac/4
      * macN/4
      * macN/5

  lib/assent/strategies/facebook.ex:122: Assent.Strategy.Facebook.appsecret_proof/2

warning: :crypto.hmac/3 is undefined or private. Did you mean one of:

      * mac/3
      * mac/4
      * macN/4
      * macN/5

  lib/assent/strategies/oauth.ex:173: Assent.Strategy.OAuth.gen_signature/6

warning: :crypto.hmac/3 is undefined or private. Did you mean one of:

      * mac/3
      * mac/4
      * macN/4
      * macN/5

  lib/assent/jwt_adapter/assent_jwt.ex:43: Assent.JWTAdapter.AssentJWT.sign_message/3

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.