Coder Social home page Coder Social logo

danschultzer / ex_oauth2_provider Goto Github PK

View Code? Open in Web Editor NEW
187.0 6.0 62.0 558 KB

Making OAuth 2 provider and authentication with http bearer as simple as possible for Elixir and Phoenix apps

License: MIT License

Elixir 100.00%
elixir oauth2-provider phoenix oauth2-server

ex_oauth2_provider's People

Contributors

aidanranney avatar danschultzer avatar davejachimiak avatar ericdude4 avatar fahchen avatar fastjames avatar fatboypunk avatar feymartynov avatar schultzer 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

ex_oauth2_provider's Issues

how to change access_token_expires_in time

Hi I want to change the default expires_in for access_token now its: 7200.
I tried with below configuration but not working.
Can you please help me to fix this.

config :phoenix_oauth2_provider, PhoenixOauth2Provider,
  module: Myapp,
  repo: Myapp.Repo,
  grant_flows: ~w(client_credentials),
  resource_owner: Myapp.Coherence.User,  
  access_token_expires_in: 14400

Schema errors during compilation

I'm getting an error with ExOauth2Provider.OauthApplications.create_application/2:

iex> oauth_application_owner = # Load MyApp.Schemas.OauthApplicationOwner
iex> ExOauth2Provider.OauthApplications.create_application(oauth_application_owner, %{
...>   name: "Oauth Application",
...>   uid: "3957adf55f16h4c88f6388d4c57357743556d7999de4ca217d5bb3e782e10726",
...>   secret: "c5604029e13afbe60ff9e7a3c6545337d7f5e9ab18dbc58ec1bbd191106afffd",
...>   redirect_uri: "https://example.com/callback_uri"
...> })
INSERT INTO "oauth_application_owners" ("name","inserted_at","updated_at","id") VALUES ($1,$2,$3,$4) ["An Application Owner", #DateTime<2019-02-28 22:20:34.400424Z>, #DateTime<2019-02-28 22:20:34.400424Z>, <<9, 63, 103, 221, 248, 34, 65, 212, 151, 146, 194, 143, 4, 23, 17, 234>>]
** (UndefinedFunctionError) function nil.__schema__/1 is undefined
    nil.__schema__(:primary_key)
    (ecto) lib/ecto/changeset/relation.ex:140: Ecto.Changeset.Relation.change/3
    (ecto) lib/ecto/changeset.ex:1132: Ecto.Changeset.put_change/7
    (ecto) lib/ecto/changeset.ex:1339: Ecto.Changeset.put_relation/5
    (ex_oauth2_provider) lib/ex_oauth2_provider/oauth_applications/oauth_applications.ex:244: ExOauth2Provider.OauthApplications.new_application_changeset/3
    (ex_oauth2_provider) lib/ex_oauth2_provider/oauth_applications/oauth_applications.ex:145: ExOauth2Provider.OauthApplications.create_application/2
    priv/repo/seeds.exs:46: (file)

From some digging, it seems as though ex_oauth2_provider isn't getting compiled correctly.

Troubleshooting

When compiling ex_oauth2_provider as a dependency in my project, I get this output:

==> ex_oauth2_provider
Compiling 37 files (.ex)

12:57:56.296 [error] You need to set a resource_owner or application_owner in your config and recompile ex_oauth2_provider!
warning: invalid association `resource_owner` in schema ExOauth2Provider.OauthAccessGrants.OauthAccessGrant: associated schema nil does not exist
  lib/ex_oauth2_provider/oauth_access_grants/oauth_access_grant.ex:1: ExOauth2Provider.OauthAccessGrants.OauthAccessGrant (module)

warning: invalid association `resource_owner` in schema ExOauth2Provider.OauthAccessTokens.OauthAccessToken: associated schema nil does not exist
  lib/ex_oauth2_provider/oauth_access_tokens/oauth_access_token.ex:1: ExOauth2Provider.OauthAccessTokens.OauthAccessToken (module)

warning: invalid association `owner` in schema ExOauth2Provider.OauthApplications.OauthApplication: associated schema nil does not exist
  lib/ex_oauth2_provider/oauth_applications/oauth_application.ex:1: ExOauth2Provider.OauthApplications.OauthApplication (module)

Generated ex_oauth2_provider app

When running the project, it seems that ex_oauth2_provider does not get compiled correctly.

iex> ExOauth2Provider.OauthApplications.OauthApplication.__schema__(:association, :owner)
%Ecto.Association.BelongsTo{
  cardinality: :one,
  defaults: [],
  field: :owner,
  on_cast: nil,
  on_replace: :raise,
  owner: ExOauth2Provider.OauthApplications.OauthApplication,
  owner_key: :owner_id,
  queryable: nil, # <-- NOTICE THIS
  related: nil, # <---- AND THIS
  related_key: :id,
  relationship: :parent,
  unique: true,
  where: []
}

After deleting _build/prod/lib/ex_oauth2_provider and running iex again:

iex> ExOauth2Provider.OauthApplications.OauthApplication.__schema__(:association, :owner)
%Ecto.Association.BelongsTo{
  cardinality: :one,
  defaults: [],
  field: :owner,
  on_cast: nil,
  on_replace: :raise,
  owner: ExOauth2Provider.OauthApplications.OauthApplication,
  owner_key: :owner_id,
  queryable: MyApp.Schemas.OauthApplicationOwner,
  related: MyApp.Schemas.OauthApplicationOwner,
  related_key: :id,
  relationship: :parent,
  unique: true,
  where: []
}

The functions works the first time I compile locally, but it doesn't work in my prod environment until I delete that build directory and recompile. I'm unable to find the different between the two environments.

Could this have to do with the upgrade to Ecto 3? Any ideas?

Thanks in advance, and thanks for making this package!

Do *not* use anonymous function instead of tuple for `:access_token_generator`, `:password_auth`, etc

Anonymous functions should absolutely never ever appear in a config file. Configuration files are loaded dynamically at code loading time, however when a release is made it gets lowered to Configuration AST and thus causes an error at release generation time due to invalid config file format. Only MFA should be used in config files, absolutely never ever should an anonymous function or closure be allowed in the config file unless it is used only at code generation time and otherwise does not get generated for releases (which would require a release configuration that does not include it).

resource/app owner schema flexibility

right now, the owner schema is assumed to have an integer type and id as the default field(as per ecto belongs_to/3) and there are at least two parts where I can see we need the modification for the very least:

  • migration script (need to be able to specify integer vs uuid or even anything else for example)
  • belongs_to stuff where we might need to pass additional opts using Ecto.Schema.belongs_to/3 so we can possibly specify custom state of our foreign key

One of the quick ideas I had was to add resource_owner_opts and application_owner_opts which I've in master...techgaun:owner-config and am using it right now for my purpose. It does not take care of migration of course but it was simple enough for me to modify generated migration script.

This works fine so far for my case (my resource owner table has uuid type with user_id as the field name). I have not looked yet at most of the code to have more clear idea but what do you recommend in this case? My solution might be fine as long as we advise users to update generated migration scripts to adjust with additional options passed to belongs_to in schema but longer term, this may feel like a hack. Thoughts?

It would be great if we can generalize for cases like this so we don't tie the resource owner to be of particular schema.

Thoughts?

Current status?

I'm looking to add an OAuth provider component to a Phoenix app I'm on, and this library looks like one of the more promising ones. What's the current status? Semi-functional? Production-ready?

In my case, it doesn't need to be bulletproof, and I'm happy to help out and contribute back. I'm just wondering if it's anywhere near functional at this point.

Thanks!

Allow customizing of scope validation per user

The Situation

I want to be able to only allow some scopes to be used by specific users.

The Problem

As far as I can see digging through the code, you can't customize validation for scopes, or token creation (aside from actual generation).

Current Solution

Right now the only solution I see is doing it on the controller level, but that seems messy and possibly has some ways around.

Refactor response handling

I'm not happy with how responses are currently handled. As an example here's how an error response is handled:

@spec authorize(Schema.t(), map(), keyword()) :: {:ok, binary()} | Response.error() | Response.redirect() | Response.native_redirect()
def authorize(resource_owner, request, config \\ []) do
case validate_response_type(request, config) do
{:error, :invalid_response_type} -> unsupported_response_type(resource_owner, request, config)
{:error, :missing_response_type} -> invalid_request(resource_owner, request, config)
{:ok, token_module} -> token_module.authorize(resource_owner, request, config)
end
end

defp unsupported_response_type(resource_owner, request, config),
do: handle_error_response(resource_owner, request, Error.unsupported_response_type(), config)

defp handle_error_response(resource_owner, request, error, config) do
resource_owner
|> Utils.prehandle_request(request, config)
|> Error.add_error(error)
|> Response.error_response(config)
end

@spec add_error({:ok, map()} | {:error, map()}, {:error, map(), atom()}) :: {:error, map()}
def add_error({:error, params}, _), do: {:error, params}
def add_error({:ok, params}, {:error, error, http_status}) do
{:error, Map.merge(params, %{error: error, error_http_status: http_status})}
end

@spec error_response({:error, map()}, keyword()) :: error() | redirect() | native_redirect()
def error_response({:error, %{error: error} = params}, config), do: build_response(params, error, config)

defp build_response(%{request: request} = params, payload, config) do
payload = add_state(payload, request)
case can_redirect?(params, config) do
true -> build_redirect_response(params, payload, config)
_ -> build_standard_response(params, payload)
end
end

From the above it's almost impossible to figure what's going on. The code is fragmented and you have to go back and fourth between several files to figure out what's going on. Also the response doesn't make much sense since it's a tuple with a map where an :error key value has been added. Not very helpful.

It should be a lot more compact an easy to read. Something along these lines, almost everything contained within this module:

  def authorize(resource_owner, request, config \\ []) do
    request
    |> validate_response_type(config)
    |> case do
      {:error, error}     -> response_type_validation_error({:error, error}, request, config)
      {:ok, token_module} -> token_authorize(token_module, resource_owner, request, config)
    end
  end

  defp response_type_validation_error({:error, error}, request, config) do
    # Return HTTP error code or build redirect response
  end

  defp token_authorize(Code, resource_owner, request, config), do: Code.authorize(resource_owner, request, config)

Upgrade issue for ecto_sql to 3.9.2

Upgrading our current ecto_sql to 3.9.2 with the current ex_oauth2_provider 0.5.6 results in compile errors due to ecto_sql now disallowing schema fields with null: false. But the ex_oauth2_provider mix.exs allows ecto_sql ~> 3.0.0 https://github.com/danschultzer/ex_oauth2_provider/blob/master/mix.exs#L45

Any calls to the function access_grant_fields() results in attempting to create the fields:

      {:token, :string, null: false},
      {:expires_in, :integer, null: false},
      {:redirect_uri, :string, null: false},

https://github.com/danschultzer/ex_oauth2_provider/blob/master/lib/ex_oauth2_provider/access_grants/access_grant.ex#L25-L31

Compile error with ecto_sql 3.9.2:

== Compilation error in file lib/engine/oauth_access_grants/oauth_access_grant.ex ==
** (ArgumentError) invalid option :null for field/3
    (ecto 3.9.4) lib/ecto/schema.ex:2227: Ecto.Schema.check_options!/3
    (ecto 3.9.4) lib/ecto/schema.ex:1926: Ecto.Schema.__field__/4
    (elixir 1.14.2) lib/enum.ex:975: Enum."-each/2-lists^foreach/1-0-"/2
    lib/engine/oauth_access_grants/oauth_access_grant.ex:14: (module)

Where is the documentation for ":secret"?

I noticed there were two ways to get a resource owner and one had a :secret atom. I have interacted with many OAuth providers before, but this is the first time I have seen this. What is a "secret location"? Where can I read more about this?

ExOauth2Provider.Plug.current_access_token(conn) # access the token in the default location
ExOauth2Provider.Plug.current_access_token(conn, :secret) # access the token in the secret location
ExOauth2Provider.Plug.current_resource_owner(conn) # Access the loaded resource owner in the default location
ExOauth2Provider.Plug.current_resource_owner(conn, :secret) # Access the loaded resource owner in the secret location

Resource Owner

I'm using the grant type client_credentials and a pipeline with ExOauth2Provider.Plug.VerifyHeader and ExOauth2Provider.Plug.EnsureAuthenticated but every time I tried to obtain the resource_owner I get a null value

Support more options of belongs_to association on OauthApplication

Now I have a schema named User which primary_key is uuid (not id), and I set it as application_owner.
When I create_application for a user, I got errors for I can't change references option of belongs_to (https://github.com/danschultzer/ex_oauth2_provider/blob/v0.4.1/lib/ex_oauth2_provider/oauth_applications/oauth_application.ex#L18). The same error happened at ExOauth2Provider.OauthAccessTokens.OauthAccessToken and ExOauth2Provider.OauthAccessGrants.OauthAccessGrant

# This works
defmodule ExOauth2Provider.OauthApplications.OauthApplication do
  # ...
  schema "oauth_applications" do
    belongs_to :owner, Config.application_owner_struct(:module), type: Config.application_owner_struct(:foreign_key_type), references: :uuid
    # ...
  end
end

I check ecto code, I found the related_key is always to :id instead of primary_key of current schema. elixir-ecto/ecto@43b564f
Can we support more options by codes like below:

config :ex_oauth2_provider, ExOauth2Provider,
  application_owner: {Dummy.User, [type: :binary_id, references: :uuid]},

defmodule ExOauth2Provider.OauthApplications.OauthApplication do
  # ...
  schema "oauth_applications" do
    belongs_to :owner, Config.application_owner_struct(:module), Config.application_owner_struct(:options)
    # ...
  end
end

Support custom url schemes for native mobile apps

As defined in the spec section 7.1 for native mobile app oauth 2.0 an authorization server should support custom url schemes for the mobile app to be able to receive the redirect on. Currently the redirectUrl validation does not support these kinds of custom url schemes.

Example taken from the spec com.example.app:/oauth2redirect/example-provider

I will point out that some platforms like iOS allow you to intercept https domains and still have the app open them also mentioned in the spec section 7.2 and is recommended over the custom url schemes but it is much easier to setup a custom url scheme than to setup support for https link interception through universal links

`Mix.env/0 is undefined` in `ExOauth2Provider.Config.force_ssl_in_redirect_uri?`

Hey. Within our production environment (via mix release) i get an error regarding Mix.env is undefined (which is obviously not available).

Log:

Request: POST /settings/applications
** (exit) an exception was raised:
    ** (UndefinedFunctionError) function Mix.env/0 is undefined (module Mix is not available)
        Mix.env()
        (ex_oauth2_provider) lib/ex_oauth2_provider/config.ex:120: ExOauth2Provider.Config.force_ssl_in_redirect_uri?/1
        (ex_oauth2_provider) lib/ex_oauth2_provider/redirect_uri.ex:40: ExOauth2Provider.RedirectURI.invalid_ssl_uri?/2
        (ex_oauth2_provider) lib/ex_oauth2_provider/redirect_uri.ex:32: ExOauth2Provider.RedirectURI.do_validate/3
        (ex_oauth2_provider) lib/ex_oauth2_provider/applications/application.ex:109: anonymous fn/3 in ExOauth2Provider.Applications.Application.validate_redirect_uri/2
        (elixir) lib/enum.ex:1948: Enum."-reduce/3-lists^foldl/2-0-"/3
        (ex_oauth2_provider) lib/ex_oauth2_provider/applications/application.ex:75: ExOauth2Provider.Applications.Application.changeset/3
        (ex_oauth2_provider) lib/ex_oauth2_provider/applications/applications.ex:159: ExOauth2Provider.Applications.create_application/3

Configuration

config :core, ExOauth2Provider, force_ssl_in_redirect_uri: true

function Mix.env/0 is undefined

Hey Dan! Getting this on prod with my new production setup (dockerized with Elixir releases). Any ideas? I've tried putting force_ssl_in_redirect_uri: true just about everywhere in the config.

Latest stuff is on this branch: https://github.com/Logflare/logflare/tree/master-instance-group

I'm using it via your phoenix_oauth2_provider package.

Thanks again!

#PID<0.5575.7> running LogflareWeb.Endpoint (connection #PID<0.22745.5>, stream id 79) terminated Server: logflare.app:80 (http) Request: GET /oauth/authorize?scope=read%20write&response_type=code&redirect_uri=https%3A%2F%2Fwww.cloudflare.com%2Fapps%2Foauth%2F&client_id=d8a8af24ada66bfa29477d746d74ae7a8833d80019c6fa285f07fe6159491a5f&user.email=chase%40logflare.app&site.name=70waggy.com ** (exit) an exception was raised: ** (UndefinedFunctionError) function Mix.env/0 is undefined (module Mix is not available) Mix.env() (ex_oauth2_provider) lib/ex_oauth2_provider/config.ex:120: ExOauth2Provider.Config.force_ssl_in_redirect_uri?/1 (ex_oauth2_provider) lib/ex_oauth2_provider/redirect_uri.ex:40: ExOauth2Provider.RedirectURI.invalid_ssl_uri?/2 (ex_oauth2_provider) lib/ex_oauth2_provider/redirect_uri.ex:32: ExOauth2Provider.RedirectURI.do_validate/3 (ex_oauth2_provider) lib/ex_oauth2_provider/redirect_uri.ex:68: ExOauth2Provider.RedirectURI.valid_for_authorization?/3 (ex_oauth2_provider) lib/ex_oauth2_provider/oauth2/authorization/strategy/code.ex:220: ExOauth2Provider.Authorization.Code.validate_redirect_uri/2 (ex_oauth2_provider) lib/ex_oauth2_provider/oauth2/authorization/strategy/code.ex:189: ExOauth2Provider.Authorization.Code.validate_request/2 (ex_oauth2_provider) lib/ex_oauth2_provider/oauth2/authorization/strategy/code.ex:92: ExOauth2Provider.Authorization.Code.preauthorize/3

Allow loading `resource_owner` to be configurable.

Right now ExOauth2Provider relies on preload/2 to load the owner from the access token: https://github.com/danschultzer/ex_oauth2_provider/blob/master/lib/ex_oauth2_provider.ex#L92-L98

In our application this less than ideal as we need to do some additional loading alongside our user and now we're forced to maintain that logic in 2 separate parts of the app.

Would you be open to a PR that made this function optionally configurable or overrideable?

User and Token coupling with Ecto models

Hey @danschultzer,

Currently ex_oauth2_provider assumes users and token are ecto models. This forces you to define and store these in a database. There's a use case where you might not want to do this, e.g. users authenticated with an external service and tokens you do not want to actually store like asymmetrically signed JWT tokens.

I'd like to try to provide customization of these logic, so that the user of the library can decide how these details can be handled through an interface. Are you willing to consider a PR going in this direction?

Revoking token does not prevent using a refresh token for a new token

Thank you for your work on this dependency.

I'm bootstrapping an application and am not sure of the source of this issue. When I create an application, then revoke the token by either deleting it from /oauth/authorized_applications or by sending a POST request to the /oauth/token specifying the inital token as a body parameter, the application is no longer listed at /oauth/authorized_applications.

Querying for the token in the database confirms that a revoked_at has been set:

select token, refresh_token, revoked_at from oauth_access_tokens limit 1;
                                                                                                                                                                                                                                                     token                                                                                                                                                                                                                                                      |                          refresh_token                           |     revoked_at
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------+---------------------
 eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJlbmJhbGFfdnBwIiwiZXhwIjoxNjEzNzcyMjU1LCJpYXQiOjE2MTM2ODU4NTUsImlzcyI6ImVuYmFsYV92cHAiLCJqdGkiOiJlZTg5NDMzOS0zMzVmLTQ4OWItYjZlOS00NGViNzQ0MWYzZmYiLCJuYmYiOjE2MTM2ODU4NTQsInNjb3BlcyI6WyJ0ZWxlbWV0cnlfd3JpdGUiXSwic3ViIjoiMTRhYzdjYWItZDcxMi00YTQyLThmYmUtYjhmOGFiZWEyZDk4IiwidHlwIjoiYWNjZXNzIiwidXNlciI6eyJpZCI6IjE0YWM3Y2FiLWQ3MTItNGE0Mi04ZmJlLWI4ZjhhYmVhMmQ5OCJ9fQ.cniknfwy7pevxC-dIGHsBfANyySMJcf4SxXTCm-yPc54Fn4NQkClQE5DuQ8WCq3aMXHTByVRHU9n0kPb2DL12A | 3690b49ef72546cbc0e6f15abe5f2920abb1f2f55530ccd556984d9a0554c3c3 | 2021-02-18 22:06:45

But then if I send a POST request to oauth/token with the above refresh token, a new token is provided and the application is then listed at oauth/authorized_applications. Note the created_at timestamp of 2021-02-18T22:08:08.519332Z which is after the revoked_at timestamp of 2021-02-18 22:06:45.

Successful POST

Any help or suggestions would be greatly appreciated.

Unable to compile using Schema for UUID

I have set up configuration in accordance with the instructions for UUID, but this causes the app to fail compilation on a clean build. The schema module is compiled after the dependency, but since the dependency requires the schema for expansion, it fails during compile.

lib/ex_oauth2_provider/oauth_access_grants/oauth_access_grant.ex:4: ExOauth2Provider.OauthAccessGrants.OauthAccessGrant (module) expanding macro: ExOauth2Provider.Schema.__using__/1

If one compiles without the configuration first, then adds the configuration and recompiles, it will work since the beam file is present then. However, this won't work for situations where builds need to occur from a clean state, such as new checkout or CI.

This also seems to be an issue with resource_owner. It fails to find the undefined module on compile here lib/ex_oauth2_provider/utils.ex:34: ExOauth2Provider.Utils.schema_belongs_to_opts/1.

Example in readme is vulnerable to timing attack

    user = Repo.get_by(User, email: username)

    cond do
      user == nil                            -> {:error, :no_user_found}
      check_pw(user.password_hash, password) -> {:ok, user}
      true                                   -> {:error, :invalid_password}
    end

Even if no user was found the pw should be checked to ensure response timings are consistent for both.

`Ecto.StaleEntryError` — attempted to delete a stale struct

Hi, Andrea from the Elixir team here 👋

We've seen this pop up in our Sentry.

CleanShot 2024-04-03 at 21 06 54@2x

We don't really own much of the stacktrace, as it's mostly generated by ex_oauth2_provider. Wondering if this is something you've seen in the past, or if anything jumps out?

Thanks for all the fantastic work 💟

Rewrite how ecto schema works

Changesets in schema module files

Right now changesets are in context file. This is incorrect behaviour.

Generate schema module files when running install script

Schema module files should be generated in similar fashion to Pow

There are too many issues with configuration during compile time (e.g. #46), and it's more appropriate to have the configuration handled by the developer in their app with the modules. It'll also handle UUID much better.

OpenID Connect layer

Hi, have you given any thought to an optional OpenID Connect layer in this library?

A default implementation proposal:

someone has requested a token with scope=openid then exchanges an authorization code for an access token.

# GET /oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=CALLBACK_URL&scope=openid
case ExOauth2Provider.Authorization.preauthorize(resource_owner, params, otp_app: :my_app) do
  {:ok, client, scopes}             -> # render authorization page
  {:redirect, redirect_uri}         -> # redirect to external redirect_uri
  {:native_redirect, %{code: code}} -> # redirect to local :show endpoint
  {:error, error, http_status}      -> # render error page
end

# Then the auth code exchange can respond like:
# POST /oauth/token?client_id=CLIENT_ID&client_secret=CLIENT_SECRET&grant_type=authorization_code&code=AUTHORIZATION_CODE&redirect_uri=CALLBACK_URL
case ExOauth2Provider.Token.grant(params, otp_app: :my_app) do
  {:ok, access_token}                             -> # JSON response
  {:ok, access_token, open_id_token}   -> # JSON response which includes openid connect JWT and access_token
  {:error, error, http_status}                   -> # JSON response
end

Maybe the authorization server can define which of the resource_owner fields should be encoded as JWT claims:

config :my_app, ExOauth2Provider,
  repo: MyApp.Repo,
  resource_owner: MyApp.Users.User
  open_id: true,
  open_id_claim_fields: [:id, :email, ...]

What do you think?

CaseClauseError when passing RefreshToken.grant a revoked token - v0.2.0

The RefreshToken strategy checks if a revoked token has been passed in, (line 78), however it seems to cause a CaseClauseError in the issue_access_token_by_refresh_token function (line 62). We do not have a test case for revoked tokens in refresh_token_test.exs, so is this a valid test case? Or are we relying on the plugs to guard against revoked tokens making their way into the app?

Application Code

defmodule MyApp.Accounts.RefreshToken do
  alias MyApp.Accounts.Services.Applications
  alias ExOauth2Provider.Token
  alias ExOauth2Provider.OauthAccessTokens.OauthAccessToken

  def call(%OauthAccessToken{} = token) do
    with {:ok, app} <- Applications.default_application(),
         {:ok, access_token} <- refresh_access_token(app, token)
    do
      {:ok, access_token}
    else
      {:error, error, _status} -> {:error, error}
      _ -> {:error, "Something went wrong"}
    end
  end
  def call(_), do: {:error, "Invalid arguments"}

  defp refresh_access_token(app, token) do
    %{
      "grant_type" => "refresh_token",
      "client_id" => app.uid,
      "client_secret" => app.secret,
      "refresh_token" => token.refresh_token
    } |> Token.grant()
  end
end

Config.exs

config :ex_oauth2_provider, ExOauth2Provider,
  repo: MyApp.Repo,
  resource_owner: MyApp.Accounts.Schemas.User,
  application_owner: MyApp.Accounts.Schemas.OauthApplicationUser,
  use_refresh_token: true,
  revoke_refresh_token_on_use: true,
  grant_flows: ~w(password refresh_token),
  access_token_expires_in: 900,
  password_auth: {MyApp.Accounts.Authenticate, :validate_user_credentials}

Test Case

test "with revoked token returns error tuple" do
  user = insert(:user)
  oauth_application = create_default_oauth_application()
  revoked_attrs = %{
    revoked_at: NaiveDateTime.utc_now(),
    resource_owner: user,
    application: oauth_application
  }
  revoked_token = insert(:oauth_access_token, revoked_attrs)

  {:error, message} = RefreshToken.call(revoked_token)
end

Stack trace

(CaseClauseError) no case clause matching: {:ok, {:error, {:error, %{error: :invalid_request, error_description: "The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed."}, :bad_request}}}`

 code: {:error, message} = RefreshToken.call(revoked_token)`

 stacktrace:
       (ex_oauth2_provider) lib/ex_oauth2_provider/oauth2/token/strategy/refresh_token.ex:62: ExOauth2Provider.Token.RefreshToken.issue_access_token_by_refresh_token/1
       (ex_oauth2_provider) lib/ex_oauth2_provider/oauth2/token/strategy/refresh_token.ex:31: ExOauth2Provider.Token.RefreshToken.grant/1
       (myapp) lib/myapp/accounts/refresh_token.ex:8: MyApp.Accounts.RefreshToken.call/1
       test/lib/myapp/accounts/refresh_token_test.exs:33: (test)

resource_owner always required for Plug.VerifyHeader

I'm trying use an application-wide bearer token (not bound to any user) to use for metadata endpoints and third party application connections.

I created a new OauthApplication with a client_id and client_secret (and set the resource_owner to the user that creates the application in the backend).

I can retrieve an access_token from the token endpoint using the client_credentials grant but I am unauthorized when going to an API endpoint. It appears resource_owner is explicitly set to nil in the client_credentials.grant flow:

client_credentials.ex is passing the client:

OauthAccessTokens.get_or_create_token(client, request["scope"], token_params)

oauth_access_tokens.ex get_or_create_token/3 explicitly sets the resource_owner to nil:

def get_or_create_token(%OauthApplication{} = application, scopes, attrs) do
    get_or_create_token(nil, application, scopes, attrs)
end

def get_or_create_token(resource_owner, application, scopes, attrs) do

The VerifyHeader plug then returns {:no_association_found} when resource_owner is nil

Is this intended or a bug?

v0.4.4 is not published

Hi Dan, it looks like v0.4.4 with Ecto 3 support is not yet published, was just wondering if that's on purpose :)

Cheers for your work!

Cannot Create Application with Empty Secret

The comment #16 (comment) describes creating an application with "" to ensure it does not require a client secret passed as part of certain grant types. However, creation using OauthApplications.create_application/2 fails with errors: [secret: {"can't be blank", [validation: :required]}]. This is corroborated in the tests as well here: https://github.com/danschultzer/ex_oauth2_provider/blob/master/test/ex_oauth2_provider/oauth_applications/oauth_applications_test.exs#L128

Should the validation not check for nil, but allow empty strings?

client_secret required

I am using a SPA application, normally the client_secret should not be required when exchanging a code against a token. How will I proceed?

Tutorial

Hi Guys,
Thanks a lot for your hard work.

It would be great if you can provide a small tutorial or reference material along with the readme.

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.