GithubHelp home page GithubHelp logo

Comments (10)

manuscrypt avatar manuscrypt commented on May 23, 2024 1

Thank you very much again for taking the time to help me integrate with my odd auth-endpoint. I am able to authenticate with ADFS using a custom strategy, containing bits and pieces of the code you provided.
I very much appreciate your efforts, thanks again.

from assent.

manuscrypt avatar manuscrypt commented on May 23, 2024

I see that i tries to fetch the access_token, but the access_token was already in the params of the first callback, so I guess, it's technically not a full authorization code flow?

I tried several combinations, this is what it's at and i am happy it reaches the callback in the Authorization-Controller...

  authorization_params: [
        response_type: "token id_token code",
        resource: "xxx",
        grant_type: "authorization_code",
        response_mode: "form_post"
      ],

from assent.

danschultzer avatar danschultzer commented on May 23, 2024

Transferred to assent repo, as it is indeed an assent issue.

Are you using :client_secret_post auth method rather than :client_secret_basic? With :client_secret_basic the authorization header is base64 encoded: https://github.com/pow-auth/assent/blob/v0.1.4/lib/assent/strategies/oauth2.ex#L142-L152

Also FYI the next release of PowAssent will have built-in support for POST callback, though adding a POST route is pretty much the same thing.

from assent.

manuscrypt avatar manuscrypt commented on May 23, 2024

Thank you for your feedback. I played around with it some more and I have two notable points:

  1. I only get the original error, when I use no auth_method or :client_secret_basic. (even tho I see it clearly done in the code you linked). when using e.g. :client_secret_jwt, it complains about invalid credentials, which leads me to point 2.

  2. I don't have a client_secret for this provider, but it seems, that I don't need one, as I get the access_token I want in the very first callback.

it's as if it needed another implementation for get_access_token with a different match, something like:
defp get_access_token(config, %{"acccess_token" => token}) do

which returns/uses the token directly, when its in the params. but i am just guessing here.

I am using a CustomProvider with use Assent.Strategy.OAuth2.Base.
I return the following (redacted) config from default_config:

[
  client_id: "redacted guid",
  client_secret: "",
  auth_method: :client_secret_jwt,
  site: "https://login.redacted.com/adfs",
  authorize_url: "https://login.redacted.com/adfs/oauth2/authorize",
  token_url: "https://login.redacted.com/adfs/oauth2/token",
  authorization_params: [
    response_type: "token id_token code", //<-- here asking for ALL the codes
    resource: "redacted guid",
    grant_type: "password", //or authorization_code
    response_mode: "form_post" //this is needed, no luck with :query or :fragment
  ],
  resource: "same redacted guid as above",
  nonce:
    16
    |> :crypto.strong_rand_bytes()
    |> Base.encode64(padding: false),  //had `true` here  as well
  strategy: Assent.Strategy.OAuth2
]

from assent.

danschultzer avatar danschultzer commented on May 23, 2024

Yeah, you are right that no secret should be used, I'll relax the OAuth2 client to not use any auth method if there is no :auth_method defined.

In the meanwhile, could you try use :client_secret_post as the :auth_method? It seems that it's the way to deal with it: https://github.com/nordvall/TokenClient/wiki/OAuth-2-Authorization-Code-grant-in-ADFS

Your config also seems to mix in some OIDC stuff, that isn't used or necessary in OAuth 2.0. This should be enough for OAuth 2.0:

[
  client_id: "redacted guid",
  client_secret: "",
  auth_method: :client_secret_post,
  site: "https://login.redacted.com/adfs",
  authorize_url: "oauth2/authorize",
  token_url: "oauth2/token",
  authorization_params: [
    response_type: "code",
    resource: "redacted guid",
    response_mode: "form_post"
  ],
  strategy: Assent.Strategy.OAuth2
]

But you can also use Assent.Strategy.OIDC instead:

[
  client_id: "redacted guid",
  client_secret: "",
  auth_method: :client_secret_post,
  site: "https://login.redacted.com/adfs",
  authorization_params: [
    response_type: "id_token code",
    resource: "redacted guid",
    response_mode: "form_post"
  ],
  strategy: Assent.Strategy.OIDC
]

from assent.

danschultzer avatar danschultzer commented on May 23, 2024

Just added a fix in #24. Try use the master branch of assent in mix.exs: {:assent, github: "pow-auth/assent", override: true}.

And use this config setting:

[
  client_id: "redacted guid",
  site: "https://login.redacted.com/adfs",
  authorize_url: "oauth2/authorize",
  token_url: "oauth2/token",
  authorization_params: [
    response_type: "code",
    resource: "redacted guid",
    response_mode: "form_post"
  ],
  strategy: Assent.Strategy.OAuth2
]

Or if you wish to use OIDC:

[
  client_id: "redacted guid",
  site: "https://login.redacted.com/adfs",
  authorization_params: [
    response_type: "id_token code",
    resource: "redacted guid",
    response_mode: "form_post"
  ],
  strategy: Assent.Strategy.OIDC
]

from assent.

manuscrypt avatar manuscrypt commented on May 23, 2024

Very cool, thank you. I did as you suggested and I am now getting the following error:

Assent.Config.MissingKeyError at POST /auth/schultzer/callback
Key `:user_url` not found in config

This information I don't have (this is a URL at the Auth-Providers site, right?). Maybe I can find it, though, I'll keep searching... but I should not actually be storing any user-identifying information.

When I try OIDC, it behaves a bit like before, asking for the :user_secret, when I supply an empty one, it complains about the Base64-encoding.

But I was able to work around it by providing my own (horrible) strategy:

the gist:

  def authorize_url(config) do
    with {:ok, redirect_uri} <- Keyword.fetch(config, :redirect_uri),
         {:ok, site} <- Keyword.fetch(config, :site),
         {:ok, client_id} <- Keyword.fetch(config, :client_id) do
      state = gen_state()
      params = authorization_params(config, client_id, state, redirect_uri)
      authorize_url = Keyword.get(config, :authorize_url, "/oauth/authorize")
      url = to_url(site, authorize_url, params)
      {:ok, %{url: url, session_params: %{state: state}}}
    end
  end

  defp authorization_params(config, client_id, state, redirect_uri) do
    params = Keyword.get(config, :authorization_params, [])

    [response_type: "access_token", client_id: client_id, state: state, redirect_uri: redirect_uri]
    |> Keyword.merge(params)
    |> List.keysort(0)
  end

  def callback(config, params, strategy \\ __MODULE__) do
    token = params["access_token"]
    {:ok, claims} = Joken.peek_claims(token)
    {:ok, %{user: claims, token: claims}}
  end

Not sure, if I return the right stuff, tho...

from assent.

danschultzer avatar danschultzer commented on May 23, 2024

Oh right, the assent strategies expects a user url so the user info can be fetched.

You can create a custom provider module that overrides the get_user/2 method. This is how the user is fetched using the id_token with OIDC:

defmodule MyApp.ADFS do
  use Assent.Strategy.OIDC.Base

  @impl true
  def default_config(config) do
    [
      site: "https://login.redacted.com/adfs",
      authorization_params: [
        response_type: "id_token code",
        response_mode: "form_post"
      ]
    ]
  end

  @impl true
  def normalize(_config, user), do: {:ok, user}

  @impl true
  def get_user(config, token) do
    case Helpers.verify_jwt(token["id_token"], nil, config) do
      {:ok, jwt}      -> {:ok, jwt.claims}
      {:error, error} -> {:error, error}
    end
  end
end
[
  client_id: "redacted guid",
  authorization_params: [
    response_type: "id_token code",
    resource: "redacted guid",
    response_mode: "form_post"
  ],
  strategy: MyApp.ADFS
]

And if you want to use OAuth2 strategy:

defmodule MyApp.ADFS do
  use Assent.Strategy.OAuth2.Base

  @impl true
  def default_config(config) do
    [
      site: "https://login.redacted.com/adfs",
      authorize_url: "/oauth2/authorize",
      token_url: "/oauth2/token",
      authorization_params: [
        response_type: "code",
        response_mode: "form_post"
      ]
    ]
  end

  @impl true
  def normalize(_config, user), do: {:ok, user}

  @impl true
  def get_user(_config, _token), do: {:ok, %{}}

  # Or if the access token has the claims:
  #
  # @impl true
  # def get_user(config, token) do
  #   case Helpers.verify_jwt(token["access_token"], nil, config) do
  #     {:ok, jwt}      -> {:ok, jwt.claims}
  #     {:error, error} -> {:error, error}
  #   end
  # end
end
[
  client_id: "redacted guid",
  authorization_params: [
    response_type: "code",
    resource: "redacted guid",
    response_mode: "form_post"
  ],
  strategy: MyApp.ADFS
]

from assent.

danschultzer avatar danschultzer commented on May 23, 2024

Oh, btw using PowAssent it's expected that a sub is returned, so the second example you should definitely use the commented get_user/2 method so you can at least get the id for the user.

from assent.

danschultzer avatar danschultzer commented on May 23, 2024

v0.1.5 released with #24 🚀

from assent.

Related Issues (20)

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.