doorkeeper-5.6.6/ 0000755 0000041 0000041 00000000000 14422352653 013725 5 ustar www-data www-data doorkeeper-5.6.6/README.md 0000644 0000041 0000041 00000023347 14422352653 015215 0 ustar www-data www-data # Doorkeeper — awesome OAuth 2 provider for your Rails / Grape app.
[](https://rubygems.org/gems/doorkeeper)
[](https://github.com/doorkeeper-gem/doorkeeper/actions/workflows/ci.yml)
[](https://codeclimate.com/github/doorkeeper-gem/doorkeeper)
[](https://coveralls.io/github/doorkeeper-gem/doorkeeper?branch=main)
[](https://houndci.com)
[](https://dashboard.guardrails.io/default/gh/doorkeeper-gem/doorkeeper)
[](https://dependabot.com)
Doorkeeper is a gem (Rails engine) that makes it easy to introduce OAuth 2 provider
functionality to your Ruby on Rails or Grape application.
Supported features:
- [The OAuth 2.0 Authorization Framework](https://datatracker.ietf.org/doc/html/rfc6749)
- [Authorization Code Flow](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1)
- [Access Token Scopes](https://datatracker.ietf.org/doc/html/rfc6749#section-3.3)
- [Refresh token](https://datatracker.ietf.org/doc/html/rfc6749#section-1.5)
- [Implicit grant](https://datatracker.ietf.org/doc/html/rfc6749#section-4.2)
- [Resource Owner Password Credentials](https://datatracker.ietf.org/doc/html/rfc6749#section-4.3)
- [Client Credentials](https://datatracker.ietf.org/doc/html/rfc6749#section-4.4)
- [OAuth 2.0 Token Revocation](https://datatracker.ietf.org/doc/html/rfc7009)
- [OAuth 2.0 Token Introspection](https://datatracker.ietf.org/doc/html/rfc7662)
- [OAuth 2.0 Threat Model and Security Considerations](https://datatracker.ietf.org/doc/html/rfc6819)
- [OAuth 2.0 for Native Apps](https://datatracker.ietf.org/doc/html/rfc8252)
- [Proof Key for Code Exchange by OAuth Public Clients](https://datatracker.ietf.org/doc/html/rfc7636)
## Table of Contents
- [Documentation](#documentation)
- [Installation](#installation)
- [Ruby on Rails](#ruby-on-rails)
- [Grape](#grape)
- [ORMs](#orms)
- [Extensions](#extensions)
- [Example Applications](#example-applications)
- [Tutorials](#tutorials)
- [Sponsors](#sponsors)
- [Development](#development)
- [Contributing](#contributing)
- [Contributors](#contributors)
- [License](#license)
## Documentation
This documentation is valid for `main` branch. Please check the documentation for the version of doorkeeper you are using in:
https://github.com/doorkeeper-gem/doorkeeper/releases.
Additionally, other resources can be found on:
- [Guides](https://doorkeeper.gitbook.io/guides/) with how-to get started and configuration documentation
- See the [Wiki](https://github.com/doorkeeper-gem/doorkeeper/wiki) with articles and other documentation
- Screencast from [railscasts.com](http://railscasts.com/): [#353
OAuth with
Doorkeeper](http://railscasts.com/episodes/353-oauth-with-doorkeeper)
- See [upgrade guides](https://github.com/doorkeeper-gem/doorkeeper/wiki/Migration-from-old-versions)
- For general questions, please post on [Stack Overflow](http://stackoverflow.com/questions/tagged/doorkeeper)
- See [SECURITY.md](SECURITY.md) for this project's security disclose
policy
## Installation
Installation depends on the framework you're using. The first step is to add the following to your Gemfile:
```ruby
gem 'doorkeeper'
```
And run `bundle install`. After this, check out the guide related to the framework you're using.
### Ruby on Rails
Doorkeeper currently supports Ruby on Rails >= 5.0. See the guide [here](https://doorkeeper.gitbook.io/guides/ruby-on-rails/getting-started).
### Grape
Guide for integration with Grape framework can be found [here](https://doorkeeper.gitbook.io/guides/grape/grape).
## ORMs
Doorkeeper supports Active Record by default, but can be configured to work with the following ORMs:
| ORM | Support via |
| :--- | :--- |
| Active Record | by default |
| MongoDB | [doorkeeper-gem/doorkeeper-mongodb](https://github.com/doorkeeper-gem/doorkeeper-mongodb) |
| Sequel | [nbulaj/doorkeeper-sequel](https://github.com/nbulaj/doorkeeper-sequel) |
| Couchbase | [acaprojects/doorkeeper-couchbase](https://github.com/acaprojects/doorkeeper-couchbase) |
| RethinkDB | [aca-labs/doorkeeper-rethinkdb](https://github.com/aca-labs/doorkeeper-rethinkdb) |
## Extensions
Extensions that are not included by default and can be installed separately.
| | Link |
| :--- | :--- |
| OpenID Connect extension | [doorkeeper-gem/doorkeeper-openid\_connect](https://github.com/doorkeeper-gem/doorkeeper-openid_connect) |
| JWT Token support | [doorkeeper-gem/doorkeeper-jwt](https://github.com/doorkeeper-gem/doorkeeper-jwt) |
| Assertion grant extension | [doorkeeper-gem/doorkeeper-grants\_assertion](https://github.com/doorkeeper-gem/doorkeeper-grants_assertion) |
| I18n translations | [doorkeeper-gem/doorkeeper-i18n](https://github.com/doorkeeper-gem/doorkeeper-i18n) |
| CIBA - Client Initiated Backchannel Authentication Flow extension | [doorkeeper-ciba](https://github.com/autoseg/doorkeeper-ciba) |
| Device Authorization Grant | [doorkeeper-device_authorization_grant](https://github.com/exop-group/doorkeeper-device_authorization_grant) |
## Example Applications
These applications show how Doorkeeper works and how to integrate with it. Start with the oAuth2 server and use the clients to connect with the server.
| Application | Link |
| :--- | :--- |
| OAuth2 Server with Doorkeeper | [doorkeeper-gem/doorkeeper-provider-app](https://github.com/doorkeeper-gem/doorkeeper-provider-app) |
| Sinatra Client connected to Provider App | [doorkeeper-gem/doorkeeper-sinatra-client](https://github.com/doorkeeper-gem/doorkeeper-sinatra-client) |
| Devise + Omniauth Client | [doorkeeper-gem/doorkeeper-devise-client](https://github.com/doorkeeper-gem/doorkeeper-devise-client) |
You may want to create a client application to
test the integration. Check out these [client
examples](https://github.com/doorkeeper-gem/doorkeeper/wiki/Example-Applications)
in our wiki or follow this [tutorial
here](https://github.com/doorkeeper-gem/doorkeeper/wiki/Testing-your-provider-with-OAuth2-gem).
## Tutorials
See [list of tutorials](https://github.com/doorkeeper-gem/doorkeeper/wiki#how-tos--tutorials) in order to learn how to use the gem or integrate it with other solutions / gems.
## Sponsors
[](#backers)
[](#sponsors)
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/doorkeeper-gem#sponsor)]
> Codecademy supports open source as part of its mission to democratize tech. Come help us build the education the world deserves: [https://codecademy.com/about/careers](https://codecademy.com/about/careers?utm_source=doorkeeper-gem)
> If you prefer not to deal with the gory details of OAuth 2, need dedicated customer support & consulting, try the cloud-based SaaS version: [https://oauth.io](https://oauth.io/?utm_source=doorkeeper-gem)
> Wealthsimple is a financial company on a mission to help everyone achieve financial freedom by providing products and advice that are accessible and affordable. Using smart technology, Wealthsimple takes financial services that are often confusing, opaque and expensive and makes them simple, transparent, and low-cost. See what Investing on Autopilot is all about: [https://www.wealthsimple.com](https://www.wealthsimple.com/?utm_source=doorkeeper-gem)
## Development
To run the local engine server:
```
bundle install
bundle exec rake doorkeeper:server
````
By default, it uses the latest Rails version with ActiveRecord. To run the
tests with a specific Rails version:
```
BUNDLE_GEMFILE=gemfiles/rails_6_0.gemfile bundle exec rake
```
You can also experiment with the changes using `bin/console`. It uses in-memory SQLite database and default
Doorkeeper config, but you can reestablish connection or reconfigure the gem if you need.
## Contributing
Want to contribute and don't know where to start? Check out [features we're
missing](https://github.com/doorkeeper-gem/doorkeeper/wiki/Supported-Features),
create [example
apps](https://github.com/doorkeeper-gem/doorkeeper/wiki/Example-Applications),
integrate the gem with your app and let us know!
Also, check out our [contributing guidelines page](CONTRIBUTING.md).
## Contributors
Thanks to all our [awesome
contributors](https://github.com/doorkeeper-gem/doorkeeper/graphs/contributors)!
## License
MIT License. Created in Applicake. Maintained by the community.
doorkeeper-5.6.6/CHANGELOG.md 0000644 0000041 0000041 00000135666 14422352653 015557 0 ustar www-data www-data # Changelog
See https://github.com/doorkeeper-gem/doorkeeper/wiki/Migration-from-old-versions for
upgrade guides.
User-visible changes worth mentioning.
## main
- [#ID] Add your PR description here.
## 5.6.6
- [#1644] Update HTTP headers.
- [#1646] Block public clients automatic authorization skip.
- [#1648] Add custom token attributes to Refresh Token Request.
- [#1649] Fixed custom_access_token_attributes related errors.
# 5.6.5
- [#1602] Allow custom data to be stored inside access grants/tokens.
- [#1634] Code refactoring for custom token attributes.
- [#1639] Add grant type validation to avoid Internal Server Error for DELETE /oauth/authorize endpoint.
# 5.6.4
- [#1633] Apply ORM configuration in #to_prepare block to avoid autoloading errors.
# 5.6.3
- [#1622] Drop support for Rubies 2.5 and 2.6
- [#1605] Fix URI validation for Ruby 3.2+.
- [#1625] Exclude endless access tokens from `StaleRecordsCleaner`.
- [#1626] Remove deprecated `active_record_options` config option.
- [#1631] Fix regression with redirect behavior after token lookup optimizations (redirect to app URI when found).
- [#1630] Special case unique index creation for refresh_token on SQL Server.
- [#1627] Lazy evaluate Doorkeeper config when loading files and executing initializers.
## 5.6.2
- [#1604] Fix fetching of the application when custom application_class defined.
## 5.6.1
- [#1593] Add support for Trilogy ActiveRecord adapter.
- [#1597] Add optional support to use the url path for the native authorization code flow. Ports forward [#1143] from 4.4.3
- [#1599] Remove unnecessarily re-fetch of application object when creating an access token.
## 5.6.0
- [#1581] Consider `token_type_hint` when searching for access token in TokensController to avoid extra database calls.
## 5.6.0.rc2
- [#1558] Fixed bug: able to obtain a token with default scopes even if they are not present in the
application scopes when using client credentials.
- [#1567] Only filter `code` parameter if authorization_code grant flow is enabled.
## 5.6.0.rc1
- [#1551] Change lazy loading for ORM to be Ruby standard autoload.
- [#1552] Remove duplicate IDs on Auth form to improve accessibility.
- [#1542] Improve performance of `Doorkeeper::AccessToken#matching_token_for` using database specific SQL time math.
**[IMPORTANT]**: API of the `Doorkeeper::AccessToken#matching_token_for` method has changed and now it returns
only **active** access tokens (previously they were just not revoked). Please remember that the idea of the
`reuse_access_token` option is to check for existing _active_ token (see configuration option description).
## 5.5.4
- [#1535] Revert changes introduced in #1528 to allow query params in `redirect_uri` as per the spec.
## 5.5.3
- [#1528] Don't allow extra query params in redirect_uri.
- [#1525] I18n source for forbidden token error is now `doorkeeper.errors.messages.forbidden_token.missing_scope`.
- [#1531] Disable `strict-loading` for Doorkeeper models by default.
- [#1532] Add support for Rails 7.
## 5.5.2
- [#1502] Drop support for Ruby 2.4 because of EOL.
- [#1504] Updated the url fragment in the comment for code documentation.
- [#1512] Fix form behavior when response mode is form_post.
- [#1511] Fix that authorization code is returned by fragment if response_mode is fragment.
## 5.5.1
- [#1496] Revoke `old_refresh_token` if `previous_refresh_token` is present.
- [#1495] Fix `respond_to` undefined in API-only mode
- [#1488] Verify client authentication for Resource Owner Password Grant when
`config.skip_client_authentication_for_password_grant` is set and the client credentials
are sent in a HTTP Basic auth header.
## 5.5.0
- [#1482] Simplify `TokenInfoController` to be overridable (extract response rendering).
- [#1478] Fix ownership association and Rake tasks when custom models configured.
- [#1477] Respect `ActiveRecord::Base.pluralize_table_names` for Doorkeeper table names.
## 5.5.0.rc2
- [#1473] Enable `Applications` and `AuthorizedApplications` controllers in API mode.
**[IMPORTANT]** you can still skip these controllers using `skip_controllers` in
`use_doorkeeper` inside `routes.rb`. Please do it in case you don't need them.
- [#1472] Fix `establish_connection` configuration for custom defined models.
- [#1471] Add support for Ruby 3.0.
- [#1469] Check if `redirect_uri` exists.
- [#1465] Memoize nil doorkeeper_token.
- [#1459] Use built-in Ruby option to remove padding in PKCE code challenge value.
- [#1457] Make owner_id a bigint for newly-generated owner migrations
- [#1452] Empty previous_refresh_token only if present.
- [#1440] Validate empty host in redirect_uri.
- [#1438] Add form post response mode.
- [#1458] Make `config.skip_client_authentication_for_password_grant` a long term configuration option.
## 5.5.0.rc1
- [#1435] Make error response not redirectable when client is unauthorized
- [#1426] Ensure ActiveRecord callbacks are executed on token revocation.
- [#1407] Remove redundant and complex to support helpers froms tests (`should_have_json`, etc).
- [#1416] Don't add introspection route if token introspection completely disabled.
- [#1410] Properly memoize `current_resource_owner` value (consider `nil` and `false` values).
- [#1415] Ignore PKCE params for non-PKCE grants.
- [#1418] Add ability to register custom OAuth Grant Flows.
- [#1420] Require client authentication for Resource Owner Password Grant as stated in OAuth RFC.
**[IMPORTANT]** you need to create a new OAuth client (`Doorkeeper::Application`) if you didn't
have it before and use client credentials in HTTP Basic auth if you previously used this grant
flow without client authentication. To opt out of this you could set the
`skip_client_authentication_for_password_grant` configuration option to `true`, but note that
this is in violation of the OAuth spec and represents a security risk.
All the users of your provider application now need to include client credentials when they use
this grant flow.
- [#1421] Add Resource Owner instance to authorization hook context for `custom_access_token_expires_in`
configuration option to allow resource owner based Access Tokens TTL.
## 5.4.0
- [#1404] Make `Doorkeeper::Application#read_attribute_for_serialization` public.
## 5.4.0.rc2
- [#1371] Add `#as_json` method and attributes serialization restriction for Application model.
Fixes information disclosure vulnerability (CVE-2020-10187).
**[IMPORTANT]** you need to re-implement `#as_json` method for Doorkeeper Application model
if you previously used `#to_json` serialization with custom options or attributes or rely on
JSON response from /oauth/applications.json or /oauth/authorized_applications.json. This change
is a breaking change which restricts serialized attributes to a very small set of columns.
- [#1395] Fix `NameError: uninitialized constant Doorkeeper::AccessToken` for Rake tasks.
- [#1397] Add `as: :doorkeeper_application` on Doorkeeper application form in order to support
custom configured application model.
- [#1400] Correctly yield the application instance to `allow_grant_flow_for_client?` config
option (fixes #1398).
- [#1402] Handle trying authorization with client credentials.
## 5.4.0.rc1
- [#1366] Sets expiry of token generated using `refresh_token` to that of original token. (Fixes #1364)
- [#1354] Add `authorize_resource_owner_for_client` option to authorize the calling user to access an application.
- [#1355] Allow to enable polymorphic Resource Owner association for Access Token & Grant
models (`use_polymorphic_resource_owner` configuration option).
**[IMPORTANT]** Review your custom patches or extensions for Doorkeeper internals if you
have such - since now Doorkeeper passes Resource Owner instance to every objects and not
just it's ID. See PR description for details.
- [#1356] Remove duplicated scopes from Access Tokens and Grants on attribute assignment.
- [#1357] Fix `Doorkeeper::OAuth::PreAuthorization#as_json` method causing
`Stack level too deep` error with AMS (fix #1312).
- [#1358] Deprecate `active_record_options` configuration option.
- [#1359] Refactor Doorkeeper configuration options DSL to make it easy to reuse it
in external extensions.
- [#1360] Increase `matching_token_for` lookup size to 10 000 and make it configurable.
- [#1371] Fix controllers to use valid classes in case Doorkeeper has custom models configured.
- [#1370] Fix revocation response for invalid token and unauthorized requests to conform with RFC 7009 (fixes #1362).
**[IMPORTANT]** now fully according to RFC 7009 nobody can do a revocation request without `client_id`
(for public clients) and `client_secret` (for private clients). Please update your apps to include that
info in the revocation request payload.
- [#1373] Make Doorkeeper routes mapper reusable in extensions.
- [#1374] Revoke and issue client credentials token in a transaction with a row lock.
- [#1384] Add context object with auth/pre_auth and issued_token for authorization hooks.
- [#1387] Add `AccessToken#create_for` and use in `RefreshTokenRequest`.
- [#1392] Fix `enable_polymorphic_resource_owner` migration template to have proper index name.
- [#1393] Improve Applications #show page with more informative data on client secret and scopes.
- [#1394] Use Ruby `autoload` feature to load Doorkeeper files.
## 5.3.3
- [#1404] Backport: Make `Doorkeeper::Application#read_attribute_for_serialization` public.
## 5.3.2
- [#1371] Backport: add `#as_json` method and attributes serialization restriction for Application model.
Fixes information disclosure vulnerability (CVE-2020-10187).
## 5.3.1
- [#1360] Backport: Increase `matching_token_for` batch lookup size to 10 000 and make it configurable.
## 5.3.0
- [#1339] Validate Resource Owner in `PasswordAccessTokenRequest` against `nil` and `false` values.
- [#1341] Fix `refresh_token_revoked_on_use` with `hash_token_secrets` enabled.
- [#1343] Fix ruby 2.7 kwargs warning in InvalidTokenResponse.
- [#1345] Allow to set custom classes for Doorkeeper models, extract reusable AR mixins.
- [#1346] Refactor `Doorkeeper::Application#to_json` into convenient `#as_json` (fix #1344).
- [#1349] Fix `Doorkeeper::Application` AR associations using an incorrect foreign key name when using a custom class.
- [#1318] Make existing token revocation for client credentials optional and disable it by default.
**[IMPORTANT]** This is a change compared to the behaviour of version 5.2.
If you were relying on access tokens being revoked once the same client
requested a new access token, reenable it with `revoke_previous_client_credentials_token` in Doorkeeper
initialization file.
## 5.2.6
- [#1404] Backport: Make `Doorkeeper::Application#read_attribute_for_serialization` public.
## 5.2.5
- [#1371] Backport: add `#as_json` method and attributes serialization restriction for Application model.
Fixes information disclosure vulnerability (CVE-2020-10187).
## 5.2.4
- [#1360] Backport: Increase `matching_token_for` batch lookup size to 10 000 and make it configurable.
## 5.2.3
- [#1334] Remove `application_secret` flash helper and `redirect_to` keyword.
- [#1331] Move redirect_uri_validator to where it is used (`Application` model).
- [#1326] Move response_type check in pre_authorization to a method to be easily to override.
- [#1329] Fix `find_in_batches` order warning.
## 5.2.2
- [#1320] Call configured `authenticate_resource_owner` method once per request.
- [#1315] Allow generation of new secret with `Doorkeeper::Application#renew_secret`.
- [#1309] Allow `Doorkeeper::Application#to_json` to work without arguments.
## 5.2.1
- [#1308] Fix flash types for `api_only` mode (no flashes for `ActionController::API`).
- [#1306] Fix interpolation of `missing_param` I18n.
## 5.2.0
- [#1305] Make `Doorkeeper::ApplicationController` to inherit from `ActionController::API` in cases
when `api_mode` enabled (fixes #1302).
## 5.2.0.rc3
- [#1298] Slice strong params so doesn't error with Rails forms.
- [#1300] Limiting access to attributes of pre_authorization.
- [#1296] Adding client_id to strong parameters.
**[IMPORTANT]** `Doorkeeper::Server#client_via_uid` was removed.
- [#1293] Move ar specific redirect uri validator to ar orm directory.
- [#1288] Allow to pass attributes to the `Doorkeeper::OAuth::PreAuthorization#as_json` method to customize
the PreAuthorization response.
- [#1286] Add ability to customize grant flows per application (OAuth client) (#1245 , #1207)
- [#1283] Allow to customize base class for `Doorkeeper::ApplicationMetalController` (new configuration
option called `base_metal_controller` (fix #1273).
- [#1277] Prevent requested scope be empty on authorization request, handle and add description for invalid request.
## 5.2.0.rc2
- [#1270] Find matching tokens in batches for `reuse_access_token` option (fix #1193).
- [#1271] Reintroduce existing token revocation for client credentials.
**[IMPORTANT]** If you rely on being able to fetch multiple access tokens from the same
client using client credentials flow, you should skip to version 5.3, where this behaviour
is deactivated by default.
- [#1269] Update initializer template documentation.
- [#1266] Use strong parameters within pre-authorization.
- [#1264] Add :before_successful_authorization and :after_successful_authorization hooks in TokensController
- [#1263] Response properly when introspection fails and fix configurations's user guide.
## 5.2.0.rc1
- [#1260], [#1262] Improve Token Introspection configuration option (access to tokens, client).
- [#1257] Add constraint configuration when using client authentication on introspection endpoint.
- [#1252] Returning `unauthorized` when the revocation of the token should not be performed due to wrong permissions.
- [#1249] Specify case sensitive uniqueness to remove Rails 6 deprecation message
- [#1248] Display the Application Secret in HTML after creating a new application even when `hash_application_secrets` is used.
- [#1248] Return the unhashed Application Secret in the JSON response after creating new application even when `hash_application_secrets` is used.
- [#1238] Better support for native app with support for custom scheme and localhost redirection.
## 5.1.2
- [#1404] Backport: Make `Doorkeeper::Application#read_attribute_for_serialization` public.
## 5.1.1
- [#1371] Backport: add `#as_json` method and attributes serialization restriction for Application model.
Fixes information disclosure vulnerability (CVE-2020-10187).
## 5.1.0
- [#1243] Add nil check operator in token checking at token introspection.
- [#1241] Explaining foreign key options for resource owner in a single place
- [#1237] Allow to set blank redirect URI if Doorkeeper configured to use redirect URI-less grant flows.
- [#1234] Fix `StaleRecordsCleaner` to properly work with big amount of records.
- [#1228] Allow to explicitly set non-expiring tokens in `custom_access_token_expires_in` configuration
option using `Float::INFINITY` return value.
- [#1224] Do not try to store token if not found by fallback hashing strategy.
- [#1223] Update Hound/Rubocop rules, correct Doorkeeper codebase to follow style-guides.
- [#1220] Drop Rails 4.2 & Ruby < 2.4 support.
## 5.1.0.rc2
- [#1208] Unify hashing implementation into secret storing strategies
**[IMPORTANT]** If you have been using the master branch of doorkeeper with bcrypt in your Gemfile.lock,
your application secrets have been hashed using BCrypt. To restore this behavior, use the initializer option
`hash_application_secrets using: 'Doorkeeper::SecretStoring::BCrypt`.
- [#1216] Add nil check to `expires_at` method.
- [#1215] Fix deprecates for Rails 6.
- [#1214] Scopes field accepts array.
- [#1209] Fix tokens validation for Token Introspection request.
- [#1202] Use correct HTTP status codes for error responses.
**[IMPORTANT]**: this change might break your application if you were relying on the previous
401 status codes, this is now a 400 by default, or a 401 for `invalid_client` and `invalid_token` errors.
- [#1201] Fix custom TTL block `client` parameter to always be an `Doorkeeper::Application` instance.
**[IMPORTANT]**: those who defined `custom_access_token_expires_in` configuration option need to check
their block implementation: if you are using `oauth_client.application` to get `Doorkeeper::Application`
instance, then you need to replace it with just `oauth_client`.
- [#1200] Increase default Doorkeeper access token value complexity (`urlsafe_base64` instead of just `hex`)
matching RFC6749/RFC6750.
**[IMPORTANT]**: this change have possible side-effects in case you have custom database constraints for
access token value, application secrets, refresh tokens or you patched Doorkeeper models and introduced
token value validations, or you are using database with case-insensitive WHERE clause like MySQL
(you can face some collisions). Before this change access token value matched `[a-f0-9]` regex, and now
it matches `[a-zA-Z0-9\-_]`. In case you have such restrictions and your don't use custom token generator
please change configuration option `default_generator_method` to `:hex`.
- [#1195] Allow to customize Token Introspection response (fixes #1194).
- [#1189] Option to set `token_reuse_limit`.
- [#1191] Try to load bcrypt for hashing of application secrets, but add fallback.
## 5.1.0.rc1
- [#1188] Use `params` instead of `request.POST` in tokens controller (fixes #1183).
- [#1182] Fix loopback IP redirect URIs to conform with RFC8252, p. 7.3 (fixes #1170).
- [#1179] Authorization Code Grant Flow without client id returns invalid_client error.
- [#1177] Allow to limit `scopes` for certain `grant_types`
- [#1176] Fix test factory support for `factory_bot_rails`
- [#1175] Internal refactor: use `scopes_string` inside `scopes`.
- [#1168] Allow optional hashing of tokens and secrets.
- [#1164] Fix error when `root_path` is not defined.
- [#1162] Fix `enforce_content_type` for requests without body.
## 5.0.3
- [#1371] Backport: add `#as_json` method and attributes serialization restriction for Application model.
Fixes information disclosure vulnerability (CVE-2020-10187).
## 5.0.2
- [#1158] Fix initializer template: change `handle_auth_errors` option
- [#1157] Remove redundant index from migration template.
## 5.0.1
- [#1154] Refactor `StaleRecordsCleaner` to be ORM agnostic.
- [#1152] Fix migration template: change resource owner data type from integer to Rails generic `references`
- [#1151] Fix Refresh Token strategy: add proper validation of client credentials both for Public & Private clients.
- [#1149] Fix for `URIChecker#valid_for_authorization?` false negative when query is blank, but `?` present.
- [#1140] Allow rendering custom errors from exceptions (issue #844). Originally opened as [#944].
- [#1138] Revert regression bug (check for token expiration in Authorizations controller so authorization
triggers every time)
## 5.0.0
- [#1127] Change the token_type initials of the Banner Token to uppercase to comply with the RFC6750 specification.
## 5.0.0.rc2
- [#1122] Fix AuthorizationsController#new error response to be in JSON format
- [#1119] Fix token revocation for OAuth apps using "implicit" grant flow
- [#1116] `AccessGrant`s will now be revoked along with `AccessToken`s when
hitting the `AuthorizedApplicationController#destroy` route.
- [#1114] Make token info endpoint's attributes consistent with token creation
- [#1108] Simple formatting of callback URLs when listing oauth applications
- [#1106] Restrict access to AdminController with 'Forbidden 403' if admin_authenticator is not
configured by developers.
## 5.0.0.rc1
- [#1103] Allow customizing use_refresh_token
- [#1089] Removed enable_pkce_without_secret configuration option
- [#1102] Expiration time based on scopes
- [#1099] All the configuration variables in `Doorkeeper.configuration` now
always return a non-nil value (`true` or `false`)
- [#1099] ORM / Query optimization: Do not revoke the refresh token if it is not enabled
in `doorkeeper.rb`
- [#996] Expiration Time Base On Grant Type
- [#997] Allow PKCE authorization_code flow as specified in RFC7636
- [#907] Fix lookup for matching tokens in certain edge-cases
- [#992] Add API option to use Doorkeeper without management views for API only
Rails applications (`api_only`)
- [#1045] Validate redirect_uri as the native URI when making authorization code requests
- [#1048] Remove deprecated `Doorkeeper#configured?`, `Doorkeeper#database_installed?`, and
`Doorkeeper#installed?` method
- [#1031] Allow public clients to authenticate without `client_secret`. Define an app as
either public or private/confidential
**[IMPORTANT]**: all the applications (clients) now are considered as private by default.
You need to manually change `confidential` column to `false` if you are using public clients,
in other case your mobile (or other) applications will not be able to authorize.
See [#1142](https://github.com/doorkeeper-gem/doorkeeper/issues/1142) for more details.
- [#1010] Add configuration to enforce configured scopes (`default_scopes` and
`optional_scopes`) for applications
- [#1060] Ensure that the native redirect_uri parameter matches with redirect_uri of the client
- [#1064] Add :before_successful_authorization and :after_successful_authorization hooks
- [#1069] Upgrade Bootstrap to 4 for Admin
- [#1068] Add rake task to cleanup databases that can become large over time
- [#1072] AuthorizationsController: Memoize strategy.authorize_response result to enable
subclasses to use the response object.
- [#1075] Call `before_successful_authorization` and `after_successful_authorization` hooks
on `create` action as well as `new`
- [#1082] Fix #916: remember routes mapping and use it required places (fix error with
customized Token Info route).
- [#1086, #1088] Fix bug with receiving default scopes in the token even if they are
not present in the application scopes (use scopes intersection).
- [#1076] Add config to enforce content type to application/x-www-form-urlencoded
- Fix bug with `force_ssl_in_redirect_uri` when it breaks existing applications with an
SSL redirect_uri.
## 4.4.3
- [#1143] Adds a config option `opt_out_native_route_change` to opt out of the breaking api
changed introduced in https://github.com/doorkeeper-gem/doorkeeper/pull/1003
## 4.4.2
- [#1130] Backport fix for native redirect_uri from 5.x.
## 4.4.1
- [#1127] Backport token type to comply with the RFC6750 specification.
- [#1125] Backport Quote surround I18n yes/no keys
## 4.4.0
- [#1120] Backport security fix from 5.x for token revocation when using public clients
**[IMPORTANT]**: all the applications (clients) now are considered as private by default.
You need to manually change `confidential` column to `false` if you are using public clients,
in other case your mobile (or other) applications will not be able to authorize.
See [#1142](https://github.com/doorkeeper-gem/doorkeeper/issues/1142) for more details.
## 4.3.2
- [#1053] Support authorizing with query params in the request `redirect_uri` if explicitly present in app's `Application#redirect_uri`
## 4.3.1
- Remove `BaseRecord` and introduce additional concern for ordering methods to fix
braking changes for Doorkeeper models.
- [#1032] Refactor BaseRequest callbacks into configurable lambdas
- [#1040] Clear mixins from ActiveRecord DSL and save only overridable API. It
allows to use this mixins in Doorkeeper ORM extensions with minimum code boilerplate.
## 4.3.0
- [#976] Fix to invalidate the second redirect URI when the first URI is the native URI
- [#1035] Allow `Application#redirect_uri=` to handle array of URIs.
- [#1036] Allow to forbid Application redirect URI's with specific rules.
- [#1029] Deprecate `order_method` and introduce `ordered_by`. Sort applications
by `created_at` in index action.
- [#1033] Allow Doorkeeper configuration option #force_ssl_in_redirect_uri to be a callable object.
- Fix Grape integration & add specs for it
- [#913] Deferred ORM (ActiveRecord) models loading
- [#943] Fix Access Token token generation when certain errors occur in custom token generators
- [#1026] Implement RFC7662 - OAuth 2.0 Token Introspection
- [#985] Generate valid migration files for Rails >= 5
- [#972] Replace Struct subclassing with block-form initialization
- [#1003] Use URL query param to pass through native redirect auth code so automated apps can find it.
**[IMPORTANT]**: Previously authorization code response route was `/oauth/authorize/`,
now it is `oauth/authorize/native?code=` (in order to help applications to automatically find the code value).
- [#868] `Scopes#&` and `Scopes#+` now take an array or any other enumerable
object.
- [#1019] Remove translation not in use: `invalid_resource_owner`.
- Use Ruby 2 hash style syntax (min required Ruby version = 2.1)
- [#948] Make Scopes.<=> work with any "other" value.
- [#974] Redirect URI is checked without query params within AuthorizationCodeRequest.
- [#1004] More explicit help text for `native_redirect_uri`.
- [#1023] Update Ruby versions and test against 2.5.0 on Travis CI.
- [#1024] Migrate from FactoryGirl to FactoryBot.
- [#1025] Improve documentation for adding foreign keys
- [#1028] Make it possible to have composite strategy names.
## 4.2.6
- [#970] Escape certain attributes in authorization forms.
## 4.2.5
- [#936] Deprecate `Doorkeeper#configured?`, `Doorkeeper#database_installed?`, and
`Doorkeeper#installed?`
- [#909] Add `InvalidTokenResponse#reason` reader method to allow read the kind
of invalid token error.
- [#928] Test against more recent Ruby versions
- Small refactorings within the codebase
- [#921] Switch to Appraisal, and test against Rails master
- [#892] Add minimum Ruby version requirement
## 4.2.0
- Security fix: Address CVE-2016-6582, implement token revocation according to
spec (tokens might not be revoked if client follows the spec).
- [#873] Add hooks to Doorkeeper::ApplicationMetalController
- [#871] Allow downstream users to better utilize doorkeeper spec factories by
eliminating name conflict on `:user` factory.
## 4.1.0
- [#845] Allow customising the `Doorkeeper::ApplicationController` base
controller
## 4.0.0
- [#834] Fix AssetNotPrecompiled error with Sprockets 4
- [#843] Revert "Fix validation error messages"
- [#847] Specify Null option to timestamps
## 4.0.0.rc4
- [#777] Add support for public client in password grant flow
- [#823] Make configuration and specs ORM independent
- [#745] Add created_at timestamp to token generation options
- [#838] Drop `Application#scopes` generator and warning, introduced for
upgrading doorkeeper from v2 to v3.
- [#801] Fix Rails 5 warning messages
- Test against Rails 5 RC1
## 4.0.0.rc3
- [#769] Revoke refresh token on access token use. To make use of the new config
add `previous_refresh_token` column to `oauth_access_tokens`:
```
rails generate doorkeeper:previous_refresh_token
```
- [#811] Toughen parameters filter with exact match
- [#813] Applications admin bugfix
- [#799] Fix Ruby Warnings
- Drop `attr_accessible` from models
### Backward incompatible changes
- [#730] Force all timezones to use UTC to prevent comparison issues.
- [#802] Remove `config.i18n.fallbacks` from engine
## 4.0.0.rc2
- Fix optional belongs_to for Rails 5
- Fix Ruby warnings
## 4.0.0.rc1
### Backward incompatible changes
- Drops support for Rails 4.1 and earlier
- Drops support for Ruby 2.0
- [#778] Bug fix: use the remaining time that a token is still valid when
building the redirect URI for the implicit grant flow
### Other changes
- [#771] Validation error messages fixes
- Adds foreign key constraints in generated migrations between tokens and
grants, and applications
- Support Rails 5
## 3.1.0
- [#736] Existing valid tokens are now reused in client_credentials flow
- [#749] Allow user to raise authorization error with custom messages.
Under `resource_owner_authenticator` block a user can
`raise Doorkeeper::Errors::DoorkeeperError.new('custom_message')`
- [#762] Check doesn’t abort the actual migration, so it runs
- [#722] `doorkeeper_forbidden_render_options` now supports returning a 404 by
specifying `respond_not_found_when_forbidden: true` in the
`doorkeeper_forbidden_render_options` method.
- [#734] Simplify and remove duplication in request strategy classes
## 3.0.1
- [#712] Wrap exchange of grant token for access token and access token refresh
in transactions
- [#704] Allow applications scopes to be mass assigned
- [#707] Fixed order of Mixin inclusion and table_name configuration in models
- [#712] Wrap access token and refresh grants in transactions
- Adds JRuby support
- Specs, views and documentation adjustments
## 3.0.0
### Other changes
- [#693] Updates `en.yml`.
## 3.0.0 (rc2)
### Backward incompatible changes
- [#678] Change application-specific scopes to take precedence over server-wide
scopes. This removes the previous behavior where the intersection between
application and server scopes was used.
### Other changes
- [#671] Fixes `NoMethodError - undefined method 'getlocal'` when calling
the /oauth/token path. Switch from using a DateTime object to update
AR to using a Time object. (Issue #668)
- [#677] Support editing application-specific scopes via the standard forms
- [#682] Pass error hash to Grape `error!`
- [#683] Generate application secret/UID if fields are blank strings
## 3.0.0 (rc1)
### Backward incompatible changes
- [#648] Extracts mongodb ORMs to
https://github.com/doorkeeper-gem/doorkeeper-mongodb. If you use ActiveRecord
you don’t need to do any change, otherwise you will need to install the new
plugin.
- [#665] `doorkeeper_unauthorized_render_options(error:)` and
`doorkeeper_forbidden_render_options(error:)` now accept `error` keyword
argument.
### Removed deprecations
- Removes `doorkeeper_for` deprecation notice.
- Remove `applications.scopes` upgrade notice.
## 2.2.2
- [#541] Fixed `undefined method attr_accessible` problem on Rails 4
(happens only when ProtectedAttributes gem is used) in #599
## 2.2.1
- [#636] `custom_access_token_expires_in` bugfixes
- [#641] syntax error fix (Issue #612)
- [#633] Send extra details to Custom Token Generator
- [#628] Refactor: improve orm adapters to ease extension
- [#637] Upgrade to rspec to 3.2
## 2.2.0 - 2015-04-19
- [#611] Allow custom access token generators to be used
- [#632] Properly fallback to `default_scopes` when no scope is specified
- [#622] Clarify that there is a logical OR between scopes for authorizing
- [#635] Upgrade to rspec 3
- [#627] i18n fallbacks to english
- Moved CHANGELOG to NEWS.md
## 2.1.4 - 2015-03-27
- [#595] HTTP spec: Add `scope` for refresh token scope param
- [#596] Limit scopes in app scopes for client credentials
- [#567] Add Grape helpers for easier integration with Grape framework
- [#606] Add custom access token expiration support for Client Credentials flow
## 2.1.3 - 2015-03-01
- [#588] Fixes scopes_match? bug that skipped authorization form in some cases
## 2.1.2 - 2015-02-25
- [#574] Remove unused update authorization route.
- [#576] Filter out sensitive parameters from logs.
- [#582] The Authorization HTTP header fields are now case insensitive.
- [#583] Database connection bugfix in certain scenarios.
- Testing improvements
## 2.1.1 - 2015-02-06
- Remove `wildcard_redirect_url` option
- [#481] Customize token flow OAuth expirations with a config lambda
- [#568] TokensController: Memoize strategy.authorize_response result to enable
subclasses to use the response object.
- [#571] Fix database initialization issues in some configurations.
- Documentation improvements
## 2.1.0 - 2015-01-13
- [#540] Include `created_at` in response.
- [#538] Check application-level scopes in client_credentials and password flow.
- [5596227] Check application scopes in AccessToken when present. Fixes a bug in
doorkeeper 2.0.0 and 2.0.1 referring to application specific scopes.
- [#534] Internationalizes doorkeeper views.
- [#545] Ensure there is a connection to the database before checking for
missing columns
- [#546] Use `Doorkeeper::` prefix when referencing `Application` to avoid
possible application model name conflict.
- [#538] Test with Rails ~> 4.2.
### Potentially backward incompatible changes
- Enable by default `authorization_code` and `client_credentials` grant flows.
Disables implicit and password grant flows by default.
- [#510, #544, 722113f] Revoked refresh token response bugfix.
## 2.0.1 - 2014-12-17
- [#525, #526, #527] Fix `ActiveRecord::NoDatabaseError` on gem load.
## 2.0.0 - 2014-12-16
### Backward incompatible changes
- [#448] Removes `doorkeeper_for` helper. Now we use
`before_action :doorkeeper_authorize!`.
- [#469] Allow client applications to restrict the set of allowable scopes.
Fixes #317. `oauth_applications` relation needs a new `scopes` string column,
non nullable, which defaults to an empty string. To add the column run:
```
rails generate doorkeeper:application_scopes
```
If you’d rather do it by hand, your ActiveRecord migration should contain:
```ruby
add_column :oauth_applications, :scopes, :string, null: false, default: ‘’
```
### Removed deprecations
- Removes `test_redirect_uri` option. It is now called `native_redirect_uri`.
- [#446] Removes `mount Doorkeeper::Engine`. Now we use `use_doorkeeper`.
### Others
- [#484] Performance improvement - avoid performing order_by when not required.
- [#450] When password is invalid in Password Credentials Grant, Doorkeeper
returned 'invalid_resource_owner' instead of 'invalid_grant', as the spec
declares. Fixes #444.
- [#452] Allows `revoked_at` to be set in the future, for future expiry.
Rationale: https://github.com/doorkeeper-gem/doorkeeper/pull/452#issuecomment-51431459
- [#480] For Implicit grant flow, access tokens can now be reused. Fixes #421.
- [#491] Reworks of @jasl's #454 and #478. ORM refactor that allows doorkeeper
to be extended more easily with unsupported ORMs. It also marks the boundaries
between shared model code and ORM specifics inside of the gem.
- [#496] Tests with Rails 4.2.
- [#489] Adds `force_ssl_in_redirect_uri` to force the usage of the HTTPS
protocol in non-native redirect uris.
- [#516] SECURITY: Adds `protect_from_forgery` to `Doorkeeper::ApplicationController`
- [#518] Fix random failures in mongodb.
---
## 1.4.2 - 2015-03-02
- [#576] Filter out sensitive parameters from logs
## 1.4.1 - 2014-12-17
- [#516] SECURITY: Adds `protect_from_forgery` to `Doorkeeper::ApplicationController`
## 1.4.0 - 2014-07-31
- internals
- [#427] Adds specs expectations.
- [#428] Error response refactor.
- [#417] Moves token validation into Access Token class.
- [#439] Removes redundant module includes.
- [#443] TokensController and TokenInfoController inherit from ActionController::Metal
- bug
- [#418] fixes #243, requests with insufficient scope now respond 403 instead
of 401. (API change)
- [#438] fixes #398, native redirect for implicit token grant bug.
- [#440] namespace fixes
- enhancements
- [#432] Keeps query parameters
## 1.3.1 - 2014-07-06
- enhancements
- [#405] Adds facade to more easily get the token from a request in a route
constraint.
- [#415] Extend Doorkeeper TokenResponse with an `after_successful_response`
callback that allows handling of `response` object.
- internals
- [#409] Deprecates `test_redirect_uri` in favor of `native_redirect_uri`.
See discussion in: [#351].
- [#411] Clean rspec deprecations. General test improvements.
- [#412] rspec line width can go longer than 80 (hound CI config).
- bug
- [#413] fixes #340, routing scope is now taken into account in redirect.
- [#401] and [#425] application is not required any longer for access_token.
## 1.3.0 - 2014-05-23
- enhancements
- [#387] Adds reuse_access_token configuration option.
## 1.2.0 - 2014-05-02
- enhancements
- [#376] Allow users to enable basic header authorization for access tokens.
- [#374] Token revocation implementation [RFC 7009]
- [#295] Only enable specific grant flows.
- internals
- [#381] Locale source fix.
- [#380] Renames `errors_for` to `doorkeeper_errors_for`.
- [#390] Style adjustments in accordance with Ruby Style Guide form
Thoughtbot.
## 1.1.0 - 2014-03-29
- enhancements
- [#336] mongoid4 support.
- [#372] Allow users to set ActiveRecord table_name_prefix/suffix options
- internals
- [#343] separate OAuth's admin and user end-point to different layouts, upgrade theme to Bootstrap 3.1.
- [#348] Move render_options in filter after `@error` has been set
## 1.0.0 - 2014-01-13
- bug (spec)
- [#228] token response `expires_in` value is now in seconds, relative to
request time
- [#296] client is optional for password grant type.
- [#319] If client credentials are present on password grant type they are validated
- [#326] If client credentials are present in refresh token they are validated
- [#326] If authenticated client does not match original client that
obtained a refresh token it responds `invalid_grant` instead of
`invalid_client`. Previous usage was invalid according to Section 5.2 of
the spec.
- [#329] access tokens' `scopes` string wa being compared against
`default_scopes` symbols, always unauthorizing.
- [#318] Include "WWW-Authenticate" header with Unauthorized responses
- enhancements
- [#293] Adds ActionController::Instrumentation in TokensController
- [#298] Support for multiple redirect_uris added.
- [#313] `AccessToken.revoke_all_for` actually revokes all non-revoked
tokens for an application/owner instead of deleting them.
- [#333] Rails 4.1 support
- internals
- Removes jQuery dependency [fixes #300][pr #312 is related]
- [#294] Client uid and secret will be generated only if not present.
- [#316] Test warnings addressed.
- [#338] Rspec 3 syntax.
---
## 0.7.4 - 2013-12-01
- bug
- Symbols instead of strings for user input.
## 0.7.3 - 2013-10-04
- enhancements
- [#204] Allow to overwrite scope in routes
- internals
- Returns only present keys in Token Response (may imply a backwards
incompatible change). https://github.com/doorkeeper-gem/doorkeeper/issues/220
- bug
- [#290] Support for Rails 4 when 'protected_attributes' gem is present.
## 0.7.2 - 2013-09-11
- enhancements
- [#272] Allow issuing multiple access_tokens for one user/application for multiple devices
- [#170] Increase length of allowed redirect URIs
- [#239] Do not try to load unavailable Request class for the current phase.
- [#273] Relax jquery-rails gem dependency
## 0.7.1 - 2013-08-30
- bug
- [#269] Rails 3.2 raised `ActiveModel::MassAssignmentSecurity::Error`.
## 0.7.0 - 2013-08-21
- enhancements
- [#229] Rails 4!
- internals
- [#203] Changing table name to be specific in column_names_with_table
- [#215] README update
- [#227] Use Rails.config.paths["config/routes"] instead of assuming "config/routes.rb" exists
- [#262] Add jquery as gem dependency
- [#263] Add a configuration for ActiveRecord.establish_connection
- Deprecation and Ruby warnings (PRs merged outside of GitHub).
## 0.6.7 - 2013-01-13
- internals
- [#188] Add IDs to the show views for integration testing [@egtann](https://github.com/egtann)
## 0.6.6 - 2013-01-04
- enhancements
- [#187] Raise error if configuration is not set
## 0.6.5 - 2012-12-26
- enhancements
- [#184] Vendor the Bootstrap CSS [@tylerhunt](https://github.com/tylerhunt)
## 0.6.4 - 2012-12-15
- bug
- [#180] Add localization to authorized_applications destroy notice [@aalvarado](https://github.com/aalvarado)
## 0.6.3 - 2012-12-07
- bugfixes
- [#163] Error response content-type header should be application/json [@ggayan](https://github.com/ggayan)
- [#175] Make token.expires_in_seconds return nil when expires_in is nil [@miyagawa](https://github.com/miyagawa)
- enhancements
- [#166, #172, #174] Behavior to automatically authorize based on a configured proc
- internals
- [#168] Using expectation syntax for controller specs [@rdsoze](https://github.com/rdsoze)
## 0.6.2 - 2012-11-10
- bugfixes
- [#162] Remove ownership columns from base migration template [@rdsoze](https://github.com/rdsoze)
## 0.6.1 - 2012-11-07
- bugfixes
- [#160] Removed |routes| argument from initializer authenticator blocks
- documentation
- [#160] Fixed description of context of authenticator blocks
## 0.6.0 - 2012-11-05
- enhancements
- Mongoid `orm` configuration accepts only :mongoid2 or :mongoid3
- Authorization endpoint does not redirect in #new action anymore. It wasn't specified by OAuth spec
- TokensController now inherits from ActionController::Metal. There might be performance upgrades
- Add link to authorization in Applications scaffold
- [#116] MongoMapper support [@carols10cents](https://github.com/carols10cents)
- [#122] Mongoid3 support [@petergoldstein](https://github.com/petergoldstein)
- [#150] Introduce test redirect uri for applications
- bugfixes
- [#157] Response token status should be `:ok`, not `:success` [@theycallmeswift](https://github.com/theycallmeswift)
- [#159] Remove ActionView::Base.field_error_proc override (fixes #145)
- internals
- Update development dependencies
- Several refactorings
- Rails/ORM are easily swichable with env vars (rails and orm)
- Travis now tests against Mongoid v2
## 0.5.0 - 2012-10-20
Official support for rubinius was removed.
- enhancements
- Configure the way access token is retrieved from request (default to bearer header)
- Authorization Code expiration time is now configurable
- Add support for mongoid
- [#78, #128, #137, #138] Application Ownership
- [#92] Allow users to skip controllers
- [#99] Remove deprecated warnings for data-\* attributes [@towerhe](https://github.com/towerhe)
- [#101] Return existing access_token for PasswordAccessTokenRequest [@benoist](https://github.com/benoist)
- [#104] Changed access token scopes example code to default_scopes and optional_scopes [@amkirwan](https://github.com/amkirwan)
- [#107] Fix typos in initializer
- [#123] i18n for validator, flash messages [@petergoldstein](https://github.com/petergoldstein)
- [#140] ActiveRecord is the default value for the ORM [@petergoldstein](https://github.com/petergoldstein)
- internals
- [#112, #120] Replacing update_attribute with update_column to eliminate deprecation warnings [@rmoriz](https://github.com/rmoriz), [@petergoldstein](https://github.com/petergoldstein)
- [#121] Updating all development dependencies to recent versions. [@petergoldstein](https://github.com/petergoldstein)
- [#144] Adding MongoDB dependency to .travis.yml [@petergoldstein](https://github.com/petergoldstein)
- [#143] Displays errors for unconfigured error messages [@timgaleckas](https://github.com/timgaleckas)
- bugfixes
- [#102] Not returning 401 when access token generation fails [@cslew](https://github.com/cslew)
- [#125] Doorkeeper is using ActiveRecord version of as_json in ORM agnostic code [@petergoldstein](https://github.com/petergoldstein)
- [#142] Prevent double submission of password based authentication [@bdurand](https://github.com/bdurand)
- documentation
- [#141] Add rack-cors middleware to readme [@gottfrois](https://github.com/gottfrois)
## 0.4.2 - 2012-06-05
- bugfixes:
- [#94] Uninitialized Constant in Password Flow
## 0.4.1 - 2012-06-02
- enhancements:
- Backport: Move doorkeeper_for extension to Filter helper
## 0.4.0 - 2012-05-26
- deprecation
- Deprecate authorization_scopes
- database changes
- AccessToken#resource_owner_id is not nullable
- enhancements
- [#83] Add Resource Owner Password Credentials flow [@jaimeiniesta](https://github.com/jaimeiniesta)
- [#76] Allow token expiration to be disabled [@mattgreen](https://github.com/mattgreen)
- [#89] Configure the way client credentials are retrieved from request
- [#b6470a] Add Client Credentials flow
- internals
- [#2ece8d, #f93778] Introduce Client and ErrorResponse classes
## 0.3.4 - 2012-05-24
- Fix attr_accessible for rails 3.2.x
## 0.3.3 - 2012-05-07
- [#86] shrink gem package size
## 0.3.2 - 2012-04-29
- enhancements
- [#54] Ignore Authorization: headers that are not Bearer [@miyagawa](https://github.com/miyagawa)
- [#58, #64] Add destroy action to applications endpoint [@jaimeiniesta](https://github.com/jaimeiniesta), [@davidfrey](https://github.com/davidfrey)
- [#63] TokensController responds with `401 unauthorized` [@jaimeiniesta](https://github.com/jaimeiniesta)
- [#67, #72] Fix for mass-assignment [@cicloid](https://github.com/cicloid)
- internals
- [#49] Add Gemnasium status image to README [@laserlemon](https://github.com/laserlemon)
- [#50] Fix typos [@tomekw](https://github.com/tomekw)
- [#51] Updated the factory_girl_rails dependency, fix expires_in response which returned a float number instead of integer [@antekpiechnik](https://github.com/antekpiechnik)
- [#62] Typos, .gitignore [@jaimeiniesta](https://github.com/jaimeiniesta)
- [#65] Change \_path redirections to \_url redirections [@jaimeiniesta](https://github.com/jaimeiniesta)
- [#75] Fix unknown method #authenticate_admin! [@mattgreen](https://github.com/mattgreen)
- Remove application link in authorized app view
## 0.3.1 - 2012-02-17
- enhancements
- [#48] Add if, else options to doorkeeper_for
- Add views generator
- internals
- Namespace models
## 0.3.0 - 2012-02-11
- enhancements
- [#17, #31] Add support for client credentials in basic auth header [@GoldsteinTechPartners](https://github.com/GoldsteinTechPartners)
- [#28] Add indices to migration [@GoldsteinTechPartners](https://github.com/GoldsteinTechPartners)
- [#29] Allow doorkeeper to run with rails 3.2 [@john-griffin](https://github.com/john-griffin)
- [#30] Improve client's redirect uri validation [@GoldsteinTechPartners](https://github.com/GoldsteinTechPartners)
- [#32] Add token (implicit grant) flow [@GoldsteinTechPartners](https://github.com/GoldsteinTechPartners)
- [#34] Add support for custom unathorized responses [@GoldsteinTechPartners](https://github.com/GoldsteinTechPartners)
- [#36] Remove repetitions from the Authorised Applications view [@carvil](https://github.com/carvil)
- When user revoke an application, all tokens for that application are revoked
- Error messages now can be translated
- Install generator copies the error messages localization file
- internals
- Fix deprecation warnings in ActiveSupport::Base64
- Remove deprecation in doorkeeper_for that handles hash arguments
- Depends on railties instead of whole rails framework
- CI now integrates with rails 3.1 and 3.2
## 0.2.0 - 2011-12-17
- enhancements
- [#4] Add authorized applications endpoint
- [#5, #11] Add access token scopes
- [#10] Add access token expiration by default
- [#9, #12] Add refresh token flow
- internals
- [#7] Improve configuration options with :default
- Improve configuration options with :builder
- Refactor config class
- Improve coverage of authorization request integration
- bug fixes
- [#6, #20] Fix access token response headers
- Fix issue with state parameter
- deprecation
- deprecate :only and :except options in doorkeeper_for
## 0.1.1 - 2011-11-30
- enhancements
- [#3] Authorization code must be short lived and single use
- [#2] Improve views provided by doorkeeper
- [#1] Skips authorization form if the client has been authorized by the resource owner
- Improve readme
- bugfixes
- Fix issue when creating the access token (wrong client id)
## 0.1.0 - 2011-11-25
- Authorization Code flow
- OAuth applications endpoint
doorkeeper-5.6.6/doorkeeper.gemspec 0000644 0000041 0000041 00000026003 14422352653 017432 0 ustar www-data www-data #########################################################
# This file has been automatically generated by gem2tgz #
#########################################################
# -*- encoding: utf-8 -*-
# stub: doorkeeper 5.6.6 ruby lib
Gem::Specification.new do |s|
s.name = "doorkeeper".freeze
s.version = "5.6.6"
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
s.metadata = { "bug_tracker_uri" => "https://github.com/doorkeeper-gem/doorkeeper/issues", "changelog_uri" => "https://github.com/doorkeeper-gem/doorkeeper/blob/main/CHANGELOG.md", "documentation_uri" => "https://doorkeeper.gitbook.io/guides/", "homepage_uri" => "https://github.com/doorkeeper-gem/doorkeeper", "source_code_uri" => "https://github.com/doorkeeper-gem/doorkeeper" } if s.respond_to? :metadata=
s.require_paths = ["lib".freeze]
s.authors = ["Felipe Elias Philipp".freeze, "Tute Costa".freeze, "Jon Moss".freeze, "Nikita Bulai".freeze]
s.date = "2023-03-29"
s.description = "Doorkeeper is an OAuth 2 provider for Rails and Grape.".freeze
s.email = ["bulaj.nikita@gmail.com".freeze]
s.files = ["CHANGELOG.md".freeze, "MIT-LICENSE".freeze, "README.md".freeze, "app/assets/stylesheets/doorkeeper/admin/application.css".freeze, "app/assets/stylesheets/doorkeeper/application.css".freeze, "app/controllers/doorkeeper/application_controller.rb".freeze, "app/controllers/doorkeeper/application_metal_controller.rb".freeze, "app/controllers/doorkeeper/applications_controller.rb".freeze, "app/controllers/doorkeeper/authorizations_controller.rb".freeze, "app/controllers/doorkeeper/authorized_applications_controller.rb".freeze, "app/controllers/doorkeeper/token_info_controller.rb".freeze, "app/controllers/doorkeeper/tokens_controller.rb".freeze, "app/helpers/doorkeeper/dashboard_helper.rb".freeze, "app/views/doorkeeper/applications/_delete_form.html.erb".freeze, "app/views/doorkeeper/applications/_form.html.erb".freeze, "app/views/doorkeeper/applications/edit.html.erb".freeze, "app/views/doorkeeper/applications/index.html.erb".freeze, "app/views/doorkeeper/applications/new.html.erb".freeze, "app/views/doorkeeper/applications/show.html.erb".freeze, "app/views/doorkeeper/authorizations/error.html.erb".freeze, "app/views/doorkeeper/authorizations/form_post.html.erb".freeze, "app/views/doorkeeper/authorizations/new.html.erb".freeze, "app/views/doorkeeper/authorizations/show.html.erb".freeze, "app/views/doorkeeper/authorized_applications/_delete_form.html.erb".freeze, "app/views/doorkeeper/authorized_applications/index.html.erb".freeze, "app/views/layouts/doorkeeper/admin.html.erb".freeze, "app/views/layouts/doorkeeper/application.html.erb".freeze, "config/locales/en.yml".freeze, "lib/doorkeeper.rb".freeze, "lib/doorkeeper/config.rb".freeze, "lib/doorkeeper/config/abstract_builder.rb".freeze, "lib/doorkeeper/config/option.rb".freeze, "lib/doorkeeper/config/validations.rb".freeze, "lib/doorkeeper/engine.rb".freeze, "lib/doorkeeper/errors.rb".freeze, "lib/doorkeeper/grant_flow.rb".freeze, "lib/doorkeeper/grant_flow/fallback_flow.rb".freeze, "lib/doorkeeper/grant_flow/flow.rb".freeze, "lib/doorkeeper/grant_flow/registry.rb".freeze, "lib/doorkeeper/grape/authorization_decorator.rb".freeze, "lib/doorkeeper/grape/helpers.rb".freeze, "lib/doorkeeper/helpers/controller.rb".freeze, "lib/doorkeeper/models/access_grant_mixin.rb".freeze, "lib/doorkeeper/models/access_token_mixin.rb".freeze, "lib/doorkeeper/models/application_mixin.rb".freeze, "lib/doorkeeper/models/concerns/accessible.rb".freeze, "lib/doorkeeper/models/concerns/expirable.rb".freeze, "lib/doorkeeper/models/concerns/expiration_time_sql_math.rb".freeze, "lib/doorkeeper/models/concerns/orderable.rb".freeze, "lib/doorkeeper/models/concerns/ownership.rb".freeze, "lib/doorkeeper/models/concerns/polymorphic_resource_owner.rb".freeze, "lib/doorkeeper/models/concerns/resource_ownerable.rb".freeze, "lib/doorkeeper/models/concerns/reusable.rb".freeze, "lib/doorkeeper/models/concerns/revocable.rb".freeze, "lib/doorkeeper/models/concerns/scopes.rb".freeze, "lib/doorkeeper/models/concerns/secret_storable.rb".freeze, "lib/doorkeeper/oauth.rb".freeze, "lib/doorkeeper/oauth/authorization/code.rb".freeze, "lib/doorkeeper/oauth/authorization/context.rb".freeze, "lib/doorkeeper/oauth/authorization/token.rb".freeze, "lib/doorkeeper/oauth/authorization/uri_builder.rb".freeze, "lib/doorkeeper/oauth/authorization_code_request.rb".freeze, "lib/doorkeeper/oauth/base_request.rb".freeze, "lib/doorkeeper/oauth/base_response.rb".freeze, "lib/doorkeeper/oauth/client.rb".freeze, "lib/doorkeeper/oauth/client/credentials.rb".freeze, "lib/doorkeeper/oauth/client_credentials/creator.rb".freeze, "lib/doorkeeper/oauth/client_credentials/issuer.rb".freeze, "lib/doorkeeper/oauth/client_credentials/validator.rb".freeze, "lib/doorkeeper/oauth/client_credentials_request.rb".freeze, "lib/doorkeeper/oauth/code_request.rb".freeze, "lib/doorkeeper/oauth/code_response.rb".freeze, "lib/doorkeeper/oauth/error.rb".freeze, "lib/doorkeeper/oauth/error_response.rb".freeze, "lib/doorkeeper/oauth/forbidden_token_response.rb".freeze, "lib/doorkeeper/oauth/helpers/scope_checker.rb".freeze, "lib/doorkeeper/oauth/helpers/unique_token.rb".freeze, "lib/doorkeeper/oauth/helpers/uri_checker.rb".freeze, "lib/doorkeeper/oauth/hooks/context.rb".freeze, "lib/doorkeeper/oauth/invalid_request_response.rb".freeze, "lib/doorkeeper/oauth/invalid_token_response.rb".freeze, "lib/doorkeeper/oauth/nonstandard.rb".freeze, "lib/doorkeeper/oauth/password_access_token_request.rb".freeze, "lib/doorkeeper/oauth/pre_authorization.rb".freeze, "lib/doorkeeper/oauth/refresh_token_request.rb".freeze, "lib/doorkeeper/oauth/scopes.rb".freeze, "lib/doorkeeper/oauth/token.rb".freeze, "lib/doorkeeper/oauth/token_introspection.rb".freeze, "lib/doorkeeper/oauth/token_request.rb".freeze, "lib/doorkeeper/oauth/token_response.rb".freeze, "lib/doorkeeper/orm/active_record.rb".freeze, "lib/doorkeeper/orm/active_record/access_grant.rb".freeze, "lib/doorkeeper/orm/active_record/access_token.rb".freeze, "lib/doorkeeper/orm/active_record/application.rb".freeze, "lib/doorkeeper/orm/active_record/mixins/access_grant.rb".freeze, "lib/doorkeeper/orm/active_record/mixins/access_token.rb".freeze, "lib/doorkeeper/orm/active_record/mixins/application.rb".freeze, "lib/doorkeeper/orm/active_record/redirect_uri_validator.rb".freeze, "lib/doorkeeper/orm/active_record/stale_records_cleaner.rb".freeze, "lib/doorkeeper/rails/helpers.rb".freeze, "lib/doorkeeper/rails/routes.rb".freeze, "lib/doorkeeper/rails/routes/abstract_router.rb".freeze, "lib/doorkeeper/rails/routes/mapper.rb".freeze, "lib/doorkeeper/rails/routes/mapping.rb".freeze, "lib/doorkeeper/rails/routes/registry.rb".freeze, "lib/doorkeeper/rake.rb".freeze, "lib/doorkeeper/rake/db.rake".freeze, "lib/doorkeeper/rake/setup.rake".freeze, "lib/doorkeeper/request.rb".freeze, "lib/doorkeeper/request/authorization_code.rb".freeze, "lib/doorkeeper/request/client_credentials.rb".freeze, "lib/doorkeeper/request/code.rb".freeze, "lib/doorkeeper/request/password.rb".freeze, "lib/doorkeeper/request/refresh_token.rb".freeze, "lib/doorkeeper/request/strategy.rb".freeze, "lib/doorkeeper/request/token.rb".freeze, "lib/doorkeeper/secret_storing/base.rb".freeze, "lib/doorkeeper/secret_storing/bcrypt.rb".freeze, "lib/doorkeeper/secret_storing/plain.rb".freeze, "lib/doorkeeper/secret_storing/sha256_hash.rb".freeze, "lib/doorkeeper/server.rb".freeze, "lib/doorkeeper/stale_records_cleaner.rb".freeze, "lib/doorkeeper/validations.rb".freeze, "lib/doorkeeper/version.rb".freeze, "lib/generators/doorkeeper/application_owner_generator.rb".freeze, "lib/generators/doorkeeper/confidential_applications_generator.rb".freeze, "lib/generators/doorkeeper/enable_polymorphic_resource_owner_generator.rb".freeze, "lib/generators/doorkeeper/install_generator.rb".freeze, "lib/generators/doorkeeper/migration_generator.rb".freeze, "lib/generators/doorkeeper/pkce_generator.rb".freeze, "lib/generators/doorkeeper/previous_refresh_token_generator.rb".freeze, "lib/generators/doorkeeper/templates/README".freeze, "lib/generators/doorkeeper/templates/add_confidential_to_applications.rb.erb".freeze, "lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb.erb".freeze, "lib/generators/doorkeeper/templates/add_previous_refresh_token_to_access_tokens.rb.erb".freeze, "lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb".freeze, "lib/generators/doorkeeper/templates/enable_polymorphic_resource_owner_migration.rb.erb".freeze, "lib/generators/doorkeeper/templates/initializer.rb".freeze, "lib/generators/doorkeeper/templates/migration.rb.erb".freeze, "lib/generators/doorkeeper/views_generator.rb".freeze, "vendor/assets/stylesheets/doorkeeper/bootstrap.min.css".freeze]
s.homepage = "https://github.com/doorkeeper-gem/doorkeeper".freeze
s.licenses = ["MIT".freeze]
s.post_install_message = "Starting from 5.5.0 RC1 Doorkeeper requires client authentication for Resource Owner Password Grant\nas stated in the OAuth RFC. You have to create a new OAuth client (Doorkeeper::Application) if you didn't\nhave it before and use client credentials in HTTP Basic auth if you previously used this grant flow without\nclient authentication. \n\nTo opt out of this you could set the \"skip_client_authentication_for_password_grant\" configuration option\nto \"true\", but note that this is in violation of the OAuth spec and represents a security risk.\n\nRead https://github.com/doorkeeper-gem/doorkeeper/issues/561#issuecomment-612857163 for more details.".freeze
s.required_ruby_version = Gem::Requirement.new(">= 2.7".freeze)
s.rubygems_version = "3.2.5".freeze
s.summary = "OAuth 2 provider for Rails and Grape".freeze
if s.respond_to? :specification_version then
s.specification_version = 4
end
if s.respond_to? :add_runtime_dependency then
s.add_development_dependency(%q.freeze, [">= 0"])
s.add_development_dependency(%q.freeze, [">= 0"])
s.add_development_dependency(%q.freeze, [">= 0"])
s.add_development_dependency(%q.freeze, ["~> 2.0"])
s.add_development_dependency(%q.freeze, ["~> 6.0"])
s.add_development_dependency(%q.freeze, ["~> 0.9.3"])
s.add_development_dependency(%q.freeze, [">= 0"])
s.add_runtime_dependency(%q.freeze, [">= 5"])
s.add_development_dependency(%q.freeze, [">= 11.3.0"])
s.add_development_dependency(%q.freeze, [">= 0"])
s.add_development_dependency(%q.freeze, [">= 0"])
else
s.add_dependency(%q.freeze, [">= 0"])
s.add_dependency(%q.freeze, [">= 0"])
s.add_dependency(%q.freeze, [">= 0"])
s.add_dependency(%q.freeze, ["~> 2.0"])
s.add_dependency(%q.freeze, ["~> 6.0"])
s.add_dependency(%q.freeze, ["~> 0.9.3"])
s.add_dependency(%q.freeze, [">= 0"])
s.add_dependency(%q.freeze, [">= 5"])
s.add_dependency(%q.freeze, [">= 11.3.0"])
s.add_dependency(%q.freeze, [">= 0"])
s.add_dependency(%q.freeze, [">= 0"])
end
end
doorkeeper-5.6.6/app/ 0000755 0000041 0000041 00000000000 14422352653 014505 5 ustar www-data www-data doorkeeper-5.6.6/app/helpers/ 0000755 0000041 0000041 00000000000 14422352653 016147 5 ustar www-data www-data doorkeeper-5.6.6/app/helpers/doorkeeper/ 0000755 0000041 0000041 00000000000 14422352653 020306 5 ustar www-data www-data doorkeeper-5.6.6/app/helpers/doorkeeper/dashboard_helper.rb 0000644 0000041 0000041 00000000775 14422352653 024132 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module DashboardHelper
def doorkeeper_errors_for(object, method)
return if object.errors[method].blank?
output = object.errors[method].map do |msg|
content_tag(:span, class: "invalid-feedback") do
msg.capitalize
end
end
safe_join(output)
end
def doorkeeper_submit_path(application)
application.persisted? ? oauth_application_path(application) : oauth_applications_path
end
end
end
doorkeeper-5.6.6/app/controllers/ 0000755 0000041 0000041 00000000000 14422352653 017053 5 ustar www-data www-data doorkeeper-5.6.6/app/controllers/doorkeeper/ 0000755 0000041 0000041 00000000000 14422352653 021212 5 ustar www-data www-data doorkeeper-5.6.6/app/controllers/doorkeeper/authorizations_controller.rb 0000644 0000041 0000041 00000007530 14422352653 027072 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
class AuthorizationsController < Doorkeeper::ApplicationController
before_action :authenticate_resource_owner!
def new
if pre_auth.authorizable?
render_success
else
render_error
end
end
def create
redirect_or_render(authorize_response)
end
def destroy
redirect_or_render(authorization.deny)
rescue Doorkeeper::Errors::InvalidTokenStrategy => e
error_response = get_error_response_from_exception(e)
if Doorkeeper.configuration.api_only
render json: error_response.body, status: :bad_request
else
render :error, locals: { error_response: error_response }
end
end
private
def render_success
if skip_authorization? || (matching_token? && pre_auth.client.application.confidential?)
redirect_or_render(authorize_response)
elsif Doorkeeper.configuration.api_only
render json: pre_auth
else
render :new
end
end
def render_error
if Doorkeeper.configuration.api_only
render json: pre_auth.error_response.body,
status: :bad_request
else
render :error, locals: { error_response: pre_auth.error_response }
end
end
# Active access token issued for the same client and resource owner with
# the same set of the scopes exists?
def matching_token?
Doorkeeper.config.access_token_model.matching_token_for(
pre_auth.client,
current_resource_owner,
pre_auth.scopes,
)
end
def redirect_or_render(auth)
if auth.redirectable?
if Doorkeeper.configuration.api_only
if pre_auth.form_post_response?
render(
json: { status: :post, redirect_uri: pre_auth.redirect_uri, body: auth.body },
status: auth.status,
)
else
render(
json: { status: :redirect, redirect_uri: auth.redirect_uri },
status: auth.status,
)
end
elsif pre_auth.form_post_response?
render :form_post
else
redirect_to auth.redirect_uri, allow_other_host: true
end
else
render json: auth.body, status: auth.status
end
end
def pre_auth
@pre_auth ||= OAuth::PreAuthorization.new(
Doorkeeper.configuration,
pre_auth_params,
current_resource_owner,
)
end
def pre_auth_params
params.slice(*pre_auth_param_fields).permit(*pre_auth_param_fields)
end
def pre_auth_param_fields
custom_access_token_attributes + %i[
client_id
code_challenge
code_challenge_method
response_type
response_mode
redirect_uri
scope
state
]
end
def custom_access_token_attributes
Doorkeeper.config.custom_access_token_attributes.map(&:to_sym)
end
def authorization
@authorization ||= strategy.request
end
def strategy
@strategy ||= server.authorization_request(pre_auth.response_type)
end
def authorize_response
@authorize_response ||= begin
return pre_auth.error_response unless pre_auth.authorizable?
context = build_context(pre_auth: pre_auth)
before_successful_authorization(context)
auth = strategy.authorize
context = build_context(auth: auth)
after_successful_authorization(context)
auth
end
end
def build_context(**attributes)
Doorkeeper::OAuth::Hooks::Context.new(**attributes)
end
def before_successful_authorization(context = nil)
Doorkeeper.config.before_successful_authorization.call(self, context)
end
def after_successful_authorization(context)
Doorkeeper.config.after_successful_authorization.call(self, context)
end
end
end
doorkeeper-5.6.6/app/controllers/doorkeeper/application_controller.rb 0000644 0000041 0000041 00000000563 14422352653 026311 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
class ApplicationController <
Doorkeeper.config.resolve_controller(:base)
include Helpers::Controller
include ActionController::MimeResponds if Doorkeeper.config.api_only
unless Doorkeeper.config.api_only
protect_from_forgery with: :exception
helper "doorkeeper/dashboard"
end
end
end
doorkeeper-5.6.6/app/controllers/doorkeeper/application_metal_controller.rb 0000644 0000041 0000041 00000000552 14422352653 027471 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
class ApplicationMetalController <
Doorkeeper.config.resolve_controller(:base_metal)
include Helpers::Controller
before_action :enforce_content_type,
if: -> { Doorkeeper.config.enforce_content_type }
ActiveSupport.run_load_hooks(:doorkeeper_metal_controller, self)
end
end
doorkeeper-5.6.6/app/controllers/doorkeeper/token_info_controller.rb 0000644 0000041 0000041 00000001062 14422352653 026134 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
class TokenInfoController < Doorkeeper::ApplicationMetalController
def show
if doorkeeper_token&.accessible?
render json: doorkeeper_token_to_json, status: :ok
else
error = OAuth::InvalidTokenResponse.new
response.headers.merge!(error.headers)
render json: error_to_json(error), status: error.status
end
end
protected
def doorkeeper_token_to_json
doorkeeper_token
end
def error_to_json(error)
error.body
end
end
end
doorkeeper-5.6.6/app/controllers/doorkeeper/tokens_controller.rb 0000644 0000041 0000041 00000013744 14422352653 025316 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
class TokensController < Doorkeeper::ApplicationMetalController
before_action :validate_presence_of_client, only: [:revoke]
def create
headers.merge!(authorize_response.headers)
render json: authorize_response.body,
status: authorize_response.status
rescue Errors::DoorkeeperError => e
handle_token_exception(e)
end
# OAuth 2.0 Token Revocation - https://datatracker.ietf.org/doc/html/rfc7009
def revoke
# The authorization server responds with HTTP status code 200 if the client
# submitted an invalid token or the token has been revoked successfully.
if token.blank?
render json: {}, status: 200
# The authorization server validates [...] and whether the token
# was issued to the client making the revocation request. If this
# validation fails, the request is refused and the client is informed
# of the error by the authorization server as described below.
elsif authorized?
revoke_token
render json: {}, status: 200
else
render json: revocation_error_response, status: :forbidden
end
end
# OAuth 2.0 Token Introspection - https://datatracker.ietf.org/doc/html/rfc7662
def introspect
introspection = OAuth::TokenIntrospection.new(server, token)
if introspection.authorized?
render json: introspection.to_json, status: 200
else
error = introspection.error_response
headers.merge!(error.headers)
render json: error.body, status: error.status
end
end
private
def validate_presence_of_client
return if Doorkeeper.config.skip_client_authentication_for_password_grant
# @see 2.1. Revocation Request
#
# The client constructs the request by including the following
# parameters using the "application/x-www-form-urlencoded" format in
# the HTTP request entity-body:
# token REQUIRED.
# token_type_hint OPTIONAL.
#
# The client also includes its authentication credentials as described
# in Section 2.3. of [RFC6749].
#
# The authorization server first validates the client credentials (in
# case of a confidential client) and then verifies whether the token
# was issued to the client making the revocation request.
return if server.client
# If this validation [client credentials / token ownership] fails, the request is
# refused and the client is informed of the error by the authorization server as
# described below.
#
# @see 2.2.1. Error Response
#
# The error presentation conforms to the definition in Section 5.2 of [RFC6749].
render json: revocation_error_response, status: :forbidden
end
# OAuth 2.0 Section 2.1 defines two client types, "public" & "confidential".
#
# RFC7009
# Section 5. Security Considerations
# A malicious client may attempt to guess valid tokens on this endpoint
# by making revocation requests against potential token strings.
# According to this specification, a client's request must contain a
# valid client_id, in the case of a public client, or valid client
# credentials, in the case of a confidential client. The token being
# revoked must also belong to the requesting client.
#
# Once a confidential client is authenticated, it must be authorized to
# revoke the provided access or refresh token. This ensures one client
# cannot revoke another's tokens.
#
# Doorkeeper determines the client type implicitly via the presence of the
# OAuth client associated with a given access or refresh token. Since public
# clients authenticate the resource owner via "password" or "implicit" grant
# types, they set the application_id as null (since the claim cannot be
# verified).
#
# https://datatracker.ietf.org/doc/html/rfc6749#section-2.1
# https://datatracker.ietf.org/doc/html/rfc7009
def authorized?
# Token belongs to specific client, so we need to check if
# authenticated client could access it.
if token.application_id? && token.application.confidential?
# We authorize client by checking token's application
server.client && server.client.application == token.application
else
# Token was issued without client, authorization unnecessary
true
end
end
def revoke_token
# The authorization server responds with HTTP status code 200 if the token
# has been revoked successfully or if the client submitted an invalid
# token
token.revoke if token&.accessible?
end
def token
@token ||=
if params[:token_type_hint] == "refresh_token"
Doorkeeper.config.access_token_model.by_refresh_token(params["token"])
else
Doorkeeper.config.access_token_model.by_token(params["token"]) ||
Doorkeeper.config.access_token_model.by_refresh_token(params["token"])
end
end
def strategy
@strategy ||= server.token_request(params[:grant_type])
end
def authorize_response
@authorize_response ||= begin
before_successful_authorization
auth = strategy.authorize
context = build_context(auth: auth)
after_successful_authorization(context) unless auth.is_a?(Doorkeeper::OAuth::ErrorResponse)
auth
end
end
def build_context(**attributes)
Doorkeeper::OAuth::Hooks::Context.new(**attributes)
end
def before_successful_authorization(context = nil)
Doorkeeper.config.before_successful_authorization.call(self, context)
end
def after_successful_authorization(context)
Doorkeeper.config.after_successful_authorization.call(self, context)
end
def revocation_error_response
error_description = I18n.t(:unauthorized, scope: %i[doorkeeper errors messages revoke])
{ error: :unauthorized_client, error_description: error_description }
end
end
end
doorkeeper-5.6.6/app/controllers/doorkeeper/authorized_applications_controller.rb 0000644 0000041 0000041 00000001611 14422352653 030725 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
class AuthorizedApplicationsController < Doorkeeper::ApplicationController
before_action :authenticate_resource_owner!
def index
@applications = Doorkeeper.config.application_model.authorized_for(current_resource_owner)
respond_to do |format|
format.html
format.json { render json: @applications, current_resource_owner: current_resource_owner }
end
end
def destroy
Doorkeeper.config.application_model.revoke_tokens_and_grants_for(
params[:id],
current_resource_owner,
)
respond_to do |format|
format.html do
redirect_to oauth_authorized_applications_url, notice: I18n.t(
:notice, scope: %i[doorkeeper flash authorized_applications destroy],
)
end
format.json { head :no_content }
end
end
end
end
doorkeeper-5.6.6/app/controllers/doorkeeper/applications_controller.rb 0000644 0000041 0000041 00000005237 14422352653 026477 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
class ApplicationsController < Doorkeeper::ApplicationController
layout "doorkeeper/admin" unless Doorkeeper.configuration.api_only
before_action :authenticate_admin!
before_action :set_application, only: %i[show edit update destroy]
def index
@applications = Doorkeeper.config.application_model.ordered_by(:created_at)
respond_to do |format|
format.html
format.json { head :no_content }
end
end
def show
respond_to do |format|
format.html
format.json { render json: @application, as_owner: true }
end
end
def new
@application = Doorkeeper.config.application_model.new
end
def create
@application = Doorkeeper.config.application_model.new(application_params)
if @application.save
flash[:notice] = I18n.t(:notice, scope: %i[doorkeeper flash applications create])
flash[:application_secret] = @application.plaintext_secret
respond_to do |format|
format.html { redirect_to oauth_application_url(@application) }
format.json { render json: @application, as_owner: true }
end
else
respond_to do |format|
format.html { render :new }
format.json do
errors = @application.errors.full_messages
render json: { errors: errors }, status: :unprocessable_entity
end
end
end
end
def edit; end
def update
if @application.update(application_params)
flash[:notice] = I18n.t(:notice, scope: i18n_scope(:update))
respond_to do |format|
format.html { redirect_to oauth_application_url(@application) }
format.json { render json: @application, as_owner: true }
end
else
respond_to do |format|
format.html { render :edit }
format.json do
errors = @application.errors.full_messages
render json: { errors: errors }, status: :unprocessable_entity
end
end
end
end
def destroy
flash[:notice] = I18n.t(:notice, scope: i18n_scope(:destroy)) if @application.destroy
respond_to do |format|
format.html { redirect_to oauth_applications_url }
format.json { head :no_content }
end
end
private
def set_application
@application = Doorkeeper.config.application_model.find(params[:id])
end
def application_params
params.require(:doorkeeper_application)
.permit(:name, :redirect_uri, :scopes, :confidential)
end
def i18n_scope(action)
%i[doorkeeper flash applications] << action
end
end
end
doorkeeper-5.6.6/app/assets/ 0000755 0000041 0000041 00000000000 14422352653 016007 5 ustar www-data www-data doorkeeper-5.6.6/app/assets/stylesheets/ 0000755 0000041 0000041 00000000000 14422352653 020363 5 ustar www-data www-data doorkeeper-5.6.6/app/assets/stylesheets/doorkeeper/ 0000755 0000041 0000041 00000000000 14422352653 022522 5 ustar www-data www-data doorkeeper-5.6.6/app/assets/stylesheets/doorkeeper/application.css 0000644 0000041 0000041 00000001620 14422352653 025536 0 ustar www-data www-data /*
*= require doorkeeper/bootstrap.min
*
*= require_self
*= require_tree .
*/
body {
background-color: #eee;
font-size: 14px;
}
#container {
background-color: #fff;
border: 1px solid #999;
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 6px;
-webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);
box-shadow: 0 3px 20px rgba(0, 0, 0, 0.3);
margin: 2em auto;
max-width: 600px;
outline: 0;
padding: 1em;
width: 80%;
}
.page-header {
margin-top: 20px;
}
#oauth-permissions {
width: 260px;
}
.actions {
border-top: 1px solid #eee;
margin-top: 1em;
padding-top: 9px;
}
.actions > form > .btn {
margin-top: 5px;
}
.separator {
color: #eee;
padding: 0 .5em;
}
.inline_block {
display: inline-block;
}
#oauth {
margin-bottom: 1em;
}
#oauth > .btn {
width: 7em;
}
td {
vertical-align: middle !important;
}
doorkeeper-5.6.6/app/assets/stylesheets/doorkeeper/admin/ 0000755 0000041 0000041 00000000000 14422352653 023612 5 ustar www-data www-data doorkeeper-5.6.6/app/assets/stylesheets/doorkeeper/admin/application.css 0000644 0000041 0000041 00000000236 14422352653 026630 0 ustar www-data www-data /*
*= require doorkeeper/bootstrap.min
*
*= require_self
*= require_tree .
*/
.doorkeeper-admin .form-group > .field_with_errors {
width: 16.66667%;
}
doorkeeper-5.6.6/app/views/ 0000755 0000041 0000041 00000000000 14422352653 015642 5 ustar www-data www-data doorkeeper-5.6.6/app/views/doorkeeper/ 0000755 0000041 0000041 00000000000 14422352653 020001 5 ustar www-data www-data doorkeeper-5.6.6/app/views/doorkeeper/authorized_applications/ 0000755 0000041 0000041 00000000000 14422352653 024725 5 ustar www-data www-data doorkeeper-5.6.6/app/views/doorkeeper/authorized_applications/_delete_form.html.erb 0000644 0000041 0000041 00000000510 14422352653 031002 0 ustar www-data www-data <%- submit_btn_css ||= 'btn btn-link' %>
<%= form_tag oauth_authorized_application_path(application), method: :delete do %>
<%= submit_tag t('doorkeeper.authorized_applications.buttons.revoke'), onclick: "return confirm('#{ t('doorkeeper.authorized_applications.confirmations.revoke') }')", class: submit_btn_css %>
<% end %>
doorkeeper-5.6.6/app/views/doorkeeper/authorized_applications/index.html.erb 0000644 0000041 0000041 00000001344 14422352653 027473 0 ustar www-data www-data
doorkeeper-5.6.6/lib/ 0000755 0000041 0000041 00000000000 14422352653 014473 5 ustar www-data www-data doorkeeper-5.6.6/lib/doorkeeper/ 0000755 0000041 0000041 00000000000 14422352653 016632 5 ustar www-data www-data doorkeeper-5.6.6/lib/doorkeeper/grape/ 0000755 0000041 0000041 00000000000 14422352653 017730 5 ustar www-data www-data doorkeeper-5.6.6/lib/doorkeeper/grape/helpers.rb 0000644 0000041 0000041 00000003022 14422352653 021714 0 ustar www-data www-data # frozen_string_literal: true
require "doorkeeper/grape/authorization_decorator"
module Doorkeeper
module Grape
# Doorkeeper helpers for Grape applications.
# Provides helpers for endpoints authorization based on defined set of scopes.
module Helpers
# These helpers are for grape >= 0.10
extend ::Grape::API::Helpers
include Doorkeeper::Rails::Helpers
# endpoint specific scopes > parameter scopes > default scopes
def doorkeeper_authorize!(*scopes)
endpoint_scopes = endpoint.route_setting(:scopes) ||
endpoint.options[:route_options][:scopes]
scopes = if endpoint_scopes
Doorkeeper::OAuth::Scopes.from_array(endpoint_scopes)
elsif scopes.present?
Doorkeeper::OAuth::Scopes.from_array(scopes)
end
super(*scopes)
end
def doorkeeper_render_error_with(error)
status_code = error_status_codes[error.status]
error!({ error: error.description }, status_code, error.headers)
end
private
def endpoint
env["api.endpoint"]
end
def doorkeeper_token
@doorkeeper_token ||= OAuth::Token.authenticate(
decorated_request,
*Doorkeeper.config.access_token_methods,
)
end
def decorated_request
AuthorizationDecorator.new(request)
end
def error_status_codes
{
unauthorized: 401,
forbidden: 403,
}
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/grape/authorization_decorator.rb 0000644 0000041 0000041 00000000631 14422352653 025217 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Grape
class AuthorizationDecorator < SimpleDelegator
def parameters
params
end
def authorization
env = __getobj__.env
env["HTTP_AUTHORIZATION"] ||
env["X-HTTP_AUTHORIZATION"] ||
env["X_HTTP_AUTHORIZATION"] ||
env["REDIRECT_X_HTTP_AUTHORIZATION"]
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/grant_flow.rb 0000644 0000041 0000041 00000002223 14422352653 021320 0 ustar www-data www-data # frozen_string_literal: true
require "doorkeeper/grant_flow/flow"
require "doorkeeper/grant_flow/fallback_flow"
require "doorkeeper/grant_flow/registry"
module Doorkeeper
module GrantFlow
extend Registry
register(
:implicit,
response_type_matches: "token",
response_mode_matches: %w[fragment form_post],
response_type_strategy: Doorkeeper::Request::Token,
)
register(
:authorization_code,
response_type_matches: "code",
response_mode_matches: %w[query fragment form_post],
response_type_strategy: Doorkeeper::Request::Code,
grant_type_matches: "authorization_code",
grant_type_strategy: Doorkeeper::Request::AuthorizationCode,
)
register(
:client_credentials,
grant_type_matches: "client_credentials",
grant_type_strategy: Doorkeeper::Request::ClientCredentials,
)
register(
:password,
grant_type_matches: "password",
grant_type_strategy: Doorkeeper::Request::Password,
)
register(
:refresh_token,
grant_type_matches: "refresh_token",
grant_type_strategy: Doorkeeper::Request::RefreshToken,
)
end
end
doorkeeper-5.6.6/lib/doorkeeper/version.rb 0000644 0000041 0000041 00000000361 14422352653 020644 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module VERSION
# Semantic versioning
MAJOR = 5
MINOR = 6
TINY = 6
PRE = nil
# Full version number
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
end
doorkeeper-5.6.6/lib/doorkeeper/rake.rb 0000644 0000041 0000041 00000000425 14422352653 020102 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Rake
class << self
def load_tasks
glob = File.join(File.absolute_path(__dir__), "rake", "*.rake")
Dir[glob].each do |rake_file|
load rake_file
end
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/helpers/ 0000755 0000041 0000041 00000000000 14422352653 020274 5 ustar www-data www-data doorkeeper-5.6.6/lib/doorkeeper/helpers/controller.rb 0000644 0000041 0000041 00000004657 14422352653 023020 0 ustar www-data www-data # frozen_string_literal: true
# Define methods that can be called in any controller that inherits from
# Doorkeeper::ApplicationMetalController or Doorkeeper::ApplicationController
module Doorkeeper
module Helpers
# Rails controller helpers.
#
module Controller
private
# :doc:
def authenticate_resource_owner!
current_resource_owner
end
# :doc:
def current_resource_owner
return @current_resource_owner if defined?(@current_resource_owner)
@current_resource_owner ||= begin
instance_eval(&Doorkeeper.config.authenticate_resource_owner)
end
end
def resource_owner_from_credentials
instance_eval(&Doorkeeper.config.resource_owner_from_credentials)
end
# :doc:
def authenticate_admin!
instance_eval(&Doorkeeper.config.authenticate_admin)
end
def server
@server ||= Server.new(self)
end
# :doc:
def doorkeeper_token
return @doorkeeper_token if defined?(@doorkeeper_token)
@doorkeeper_token ||= OAuth::Token.authenticate(request, *config_methods)
end
def config_methods
@config_methods ||= Doorkeeper.config.access_token_methods
end
def get_error_response_from_exception(exception)
if exception.respond_to?(:response)
exception.response
elsif exception.type == :invalid_request
OAuth::InvalidRequestResponse.new(
name: exception.type,
state: params[:state],
missing_param: exception.missing_param,
)
else
OAuth::ErrorResponse.new(name: exception.type, state: params[:state])
end
end
def handle_token_exception(exception)
error = get_error_response_from_exception(exception)
headers.merge!(error.headers)
self.response_body = error.body.to_json
self.status = error.status
end
def skip_authorization?
!!instance_exec(
[server.current_resource_owner, @pre_auth.client],
&Doorkeeper.config.skip_authorization
)
end
def enforce_content_type
if (request.put? || request.post? || request.patch?) && !x_www_form_urlencoded?
render json: {}, status: :unsupported_media_type
end
end
def x_www_form_urlencoded?
request.media_type == "application/x-www-form-urlencoded"
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/secret_storing/ 0000755 0000041 0000041 00000000000 14422352653 021664 5 ustar www-data www-data doorkeeper-5.6.6/lib/doorkeeper/secret_storing/base.rb 0000644 0000041 0000041 00000004121 14422352653 023121 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module SecretStoring
##
# Base class for secret storing, including common helpers
class Base
##
# Return the value to be stored by the database
# used for looking up a database value.
# @param plain_secret The plain secret input / generated
def self.transform_secret(_plain_secret)
raise NotImplementedError
end
##
# Transform and store the given secret attribute => value
# pair used for safely storing the attribute
# @param resource The model instance being modified
# @param attribute The secret attribute
# @param plain_secret The plain secret input / generated
def self.store_secret(resource, attribute, plain_secret)
transformed_value = transform_secret(plain_secret)
resource.public_send(:"#{attribute}=", transformed_value)
transformed_value
end
##
# Return the restored value from the database
# @param resource The resource instance to act on
# @param attribute The secret attribute to restore
# as retrieved from the database.
def self.restore_secret(_resource, _attribute)
raise NotImplementedError
end
##
# Determines whether this strategy supports restoring
# secrets from the database. This allows detecting users
# trying to use a non-restorable strategy with +reuse_access_tokens+.
def self.allows_restoring_secrets?
false
end
##
# Determines what secrets this strategy is applicable for
def self.validate_for(model)
valid = %i[token application]
return true if valid.include?(model.to_sym)
raise ArgumentError, "'#{name}' can not be used for #{model}."
end
##
# Securely compare the given +input+ value with a +stored+ value
# processed by +transform_secret+.
def self.secret_matches?(input, stored)
transformed_input = transform_secret(input)
ActiveSupport::SecurityUtils.secure_compare transformed_input, stored
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/secret_storing/sha256_hash.rb 0000644 0000041 0000041 00000001411 14422352653 024221 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module SecretStoring
##
# Plain text secret storing, which is the default
# but also provides fallback lookup if
# other secret storing mechanisms are enabled.
class Sha256Hash < Base
##
# Return the value to be stored by the database
# @param plain_secret The plain secret input / generated
def self.transform_secret(plain_secret)
::Digest::SHA256.hexdigest plain_secret
end
##
# Determines whether this strategy supports restoring
# secrets from the database. This allows detecting users
# trying to use a non-restorable strategy with +reuse_access_tokens+.
def self.allows_restoring_secrets?
false
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/secret_storing/bcrypt.rb 0000644 0000041 0000041 00000003224 14422352653 023515 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module SecretStoring
##
# Plain text secret storing, which is the default
# but also provides fallback lookup if
# other secret storing mechanisms are enabled.
class BCrypt < Base
##
# Return the value to be stored by the database
# @param plain_secret The plain secret input / generated
def self.transform_secret(plain_secret)
::BCrypt::Password.create(plain_secret.to_s)
end
##
# Securely compare the given +input+ value with a +stored+ value
# processed by +transform_secret+.
def self.secret_matches?(input, stored)
::BCrypt::Password.new(stored.to_s) == input.to_s
rescue ::BCrypt::Errors::InvalidHash
false
end
##
# Determines whether this strategy supports restoring
# secrets from the database. This allows detecting users
# trying to use a non-restorable strategy with +reuse_access_tokens+.
def self.allows_restoring_secrets?
false
end
##
# Determines what secrets this strategy is applicable for
def self.validate_for(model)
unless model.to_sym == :application
raise ArgumentError,
"'#{name}' can only be used for storing application secrets."
end
unless bcrypt_present?
raise ArgumentError,
"'#{name}' requires the 'bcrypt' gem being loaded."
end
true
end
##
# Test if we can require the BCrypt gem
def self.bcrypt_present?
require "bcrypt"
true
rescue LoadError
false
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/secret_storing/plain.rb 0000644 0000041 0000041 00000001614 14422352653 023316 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module SecretStoring
##
# Plain text secret storing, which is the default
# but also provides fallback lookup if
# other secret storing mechanisms are enabled.
class Plain < Base
##
# Return the value to be stored by the database
# @param plain_secret The plain secret input / generated
def self.transform_secret(plain_secret)
plain_secret
end
##
# Return the restored value from the database
# @param resource The resource instance to act on
# @param attribute The secret attribute to restore
# as retrieved from the database.
def self.restore_secret(resource, attribute)
resource.public_send(attribute)
end
##
# Plain values obviously allow restoring
def self.allows_restoring_secrets?
true
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/models/ 0000755 0000041 0000041 00000000000 14422352653 020115 5 ustar www-data www-data doorkeeper-5.6.6/lib/doorkeeper/models/access_token_mixin.rb 0000644 0000041 0000041 00000037165 14422352653 024323 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module AccessTokenMixin
extend ActiveSupport::Concern
include OAuth::Helpers
include Models::Expirable
include Models::Reusable
include Models::Revocable
include Models::Accessible
include Models::Orderable
include Models::SecretStorable
include Models::Scopes
include Models::ResourceOwnerable
include Models::ExpirationTimeSqlMath
module ClassMethods
# Returns an instance of the Doorkeeper::AccessToken with
# specific plain text token value.
#
# @param token [#to_s]
# Plain text token value (any object that responds to `#to_s`)
#
# @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
# if there is no record with such token
#
def by_token(token)
find_by_plaintext_token(:token, token)
end
# Returns an instance of the Doorkeeper::AccessToken
# with specific token value.
#
# @param refresh_token [#to_s]
# refresh token value (any object that responds to `#to_s`)
#
# @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
# if there is no record with such refresh token
#
def by_refresh_token(refresh_token)
find_by_plaintext_token(:refresh_token, refresh_token)
end
# Returns an instance of the Doorkeeper::AccessToken
# found by previous refresh token. Keep in mind that value
# of the previous_refresh_token isn't encrypted using
# secrets strategy.
#
# @param previous_refresh_token [#to_s]
# previous refresh token value (any object that responds to `#to_s`)
#
# @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
# if there is no record with such refresh token
#
def by_previous_refresh_token(previous_refresh_token)
find_by(refresh_token: previous_refresh_token)
end
# Revokes AccessToken records that have not been revoked and associated
# with the specific Application and Resource Owner.
#
# @param application_id [Integer]
# ID of the Application
# @param resource_owner [ActiveRecord::Base, Integer]
# instance of the Resource Owner model or it's ID
#
def revoke_all_for(application_id, resource_owner, clock = Time)
by_resource_owner(resource_owner)
.where(
application_id: application_id,
revoked_at: nil,
)
.update_all(revoked_at: clock.now.utc)
end
# Looking for not revoked Access Token with a matching set of scopes
# that belongs to specific Application and Resource Owner.
#
# @param application [Doorkeeper::Application]
# Application instance
# @param resource_owner [ActiveRecord::Base, Integer]
# Resource Owner model instance or it's ID
# @param scopes [String, Doorkeeper::OAuth::Scopes]
# set of scopes
#
# @return [Doorkeeper::AccessToken, nil] Access Token instance or
# nil if matching record was not found
#
def matching_token_for(application, resource_owner, scopes, include_expired: true)
tokens = authorized_tokens_for(application&.id, resource_owner)
tokens = tokens.not_expired unless include_expired
find_matching_token(tokens, application, scopes)
end
# Interface to enumerate access token records in batches in order not
# to bloat the memory. Could be overloaded in any ORM extension.
#
def find_access_token_in_batches(relation, **args, &block)
relation.find_in_batches(**args, &block)
end
# Enumerates AccessToken records in batches to find a matching token.
# Batching is required in order not to pollute the memory if Application
# has huge amount of associated records.
#
# ActiveRecord 5.x - 6.x ignores custom ordering so we can't perform a
# database sort by created_at, so we need to load all the matching records,
# sort them and find latest one.
#
# @param relation [ActiveRecord::Relation]
# Access tokens relation
# @param application [Doorkeeper::Application]
# Application instance
# @param scopes [String, Doorkeeper::OAuth::Scopes]
# set of scopes
#
# @return [Doorkeeper::AccessToken, nil] Access Token instance or
# nil if matching record was not found
#
def find_matching_token(relation, application, scopes)
return nil unless relation
matching_tokens = []
batch_size = Doorkeeper.configuration.token_lookup_batch_size
find_access_token_in_batches(relation, batch_size: batch_size) do |batch|
tokens = batch.select do |token|
scopes_match?(token.scopes, scopes, application&.scopes)
end
matching_tokens.concat(tokens)
end
matching_tokens.max_by(&:created_at)
end
# Checks whether the token scopes match the scopes from the parameters
#
# @param token_scopes [#to_s]
# set of scopes (any object that responds to `#to_s`)
# @param param_scopes [Doorkeeper::OAuth::Scopes]
# scopes from params
# @param app_scopes [Doorkeeper::OAuth::Scopes]
# Application scopes
#
# @return [Boolean] true if the param scopes match the token scopes,
# and all the param scopes are defined in the application (or in the
# server configuration if the application doesn't define any scopes),
# and false in other cases
#
def scopes_match?(token_scopes, param_scopes, app_scopes)
return true if token_scopes.empty? && param_scopes.empty?
(token_scopes.sort == param_scopes.sort) &&
Doorkeeper::OAuth::Helpers::ScopeChecker.valid?(
scope_str: param_scopes.to_s,
server_scopes: Doorkeeper.config.scopes,
app_scopes: app_scopes,
)
end
# Looking for not expired AccessToken record with a matching set of
# scopes that belongs to specific Application and Resource Owner.
# If it doesn't exists - then creates it.
#
# @param application [Doorkeeper::Application]
# Application instance
# @param resource_owner [ActiveRecord::Base, Integer]
# Resource Owner model instance or it's ID
# @param scopes [#to_s]
# set of scopes (any object that responds to `#to_s`)
# @param token_attributes [Hash]
# Additional attributes to use when creating a token
# @option token_attributes [Integer] :expires_in
# token lifetime in seconds
# @option token_attributes [Boolean] :use_refresh_token
# whether to use the refresh token
#
# @return [Doorkeeper::AccessToken] existing record or a new one
#
def find_or_create_for(application:, resource_owner:, scopes:, **token_attributes)
if Doorkeeper.config.reuse_access_token
access_token = matching_token_for(application, resource_owner, scopes, include_expired: false)
return access_token if access_token&.reusable?
end
create_for(
application: application,
resource_owner: resource_owner,
scopes: scopes,
**token_attributes,
)
end
# Creates a not expired AccessToken record with a matching set of
# scopes that belongs to specific Application and Resource Owner.
#
# @param application [Doorkeeper::Application]
# Application instance
# @param resource_owner [ActiveRecord::Base, Integer]
# Resource Owner model instance or it's ID
# @param scopes [#to_s]
# set of scopes (any object that responds to `#to_s`)
# @param token_attributes [Hash]
# Additional attributes to use when creating a token
# @option token_attributes [Integer] :expires_in
# token lifetime in seconds
# @option token_attributes [Boolean] :use_refresh_token
# whether to use the refresh token
#
# @return [Doorkeeper::AccessToken] new access token
#
def create_for(application:, resource_owner:, scopes:, **token_attributes)
token_attributes[:application] = application
token_attributes[:scopes] = scopes.to_s
if Doorkeeper.config.polymorphic_resource_owner?
token_attributes[:resource_owner] = resource_owner
else
token_attributes[:resource_owner_id] = resource_owner_id_for(resource_owner)
end
create!(token_attributes)
end
# Looking for not revoked Access Token records that belongs to specific
# Application and Resource Owner.
#
# @param application_id [Integer]
# ID of the Application model instance
# @param resource_owner [ActiveRecord::Base, Integer]
# Resource Owner model instance or it's ID
#
# @return [ActiveRecord::Relation]
# collection of matching AccessToken objects
#
def authorized_tokens_for(application_id, resource_owner)
by_resource_owner(resource_owner).where(
application_id: application_id,
revoked_at: nil,
)
end
# Convenience method for backwards-compatibility, return the last
# matching token for the given Application and Resource Owner.
#
# @param application_id [Integer]
# ID of the Application model instance
# @param resource_owner [ActiveRecord::Base, Integer]
# ID of the Resource Owner model instance
#
# @return [Doorkeeper::AccessToken, nil] matching AccessToken object or
# nil if nothing was found
#
def last_authorized_token_for(application_id, resource_owner)
authorized_tokens_for(application_id, resource_owner)
.ordered_by(:created_at, :desc)
.first
end
##
# Determines the secret storing transformer
# Unless configured otherwise, uses the plain secret strategy
#
# @return [Doorkeeper::SecretStoring::Base]
#
def secret_strategy
::Doorkeeper.config.token_secret_strategy
end
##
# Determine the fallback storing strategy
# Unless configured, there will be no fallback
def fallback_secret_strategy
::Doorkeeper.config.token_secret_fallback_strategy
end
end
# Access Token type: Bearer.
# @see https://datatracker.ietf.org/doc/html/rfc6750
# The OAuth 2.0 Authorization Framework: Bearer Token Usage
#
def token_type
"Bearer"
end
def use_refresh_token?
@use_refresh_token ||= false
!!@use_refresh_token
end
# JSON representation of the Access Token instance.
#
# @return [Hash] hash with token data
def as_json(_options = {})
{
resource_owner_id: resource_owner_id,
scope: scopes,
expires_in: expires_in_seconds,
application: { uid: application.try(:uid) },
created_at: created_at.to_i,
}.tap do |json|
if Doorkeeper.configuration.polymorphic_resource_owner?
json[:resource_owner_type] = resource_owner_type
end
end
end
# Indicates whether the token instance have the same credential
# as the other Access Token.
#
# @param access_token [Doorkeeper::AccessToken] other token
#
# @return [Boolean] true if credentials are same of false in other cases
#
def same_credential?(access_token)
application_id == access_token.application_id &&
same_resource_owner?(access_token)
end
# Indicates whether the token instance have the same credential
# as the other Access Token.
#
# @param access_token [Doorkeeper::AccessToken] other token
#
# @return [Boolean] true if credentials are same of false in other cases
#
def same_resource_owner?(access_token)
if Doorkeeper.configuration.polymorphic_resource_owner?
resource_owner == access_token.resource_owner
else
resource_owner_id == access_token.resource_owner_id
end
end
# Indicates if token is acceptable for specific scopes.
#
# @param scopes [Array] scopes
#
# @return [Boolean] true if record is accessible and includes scopes or
# false in other cases
#
def acceptable?(scopes)
accessible? && includes_scope?(*scopes)
end
# We keep a volatile copy of the raw refresh token for initial communication
# The stored refresh_token may be mapped and not available in cleartext.
def plaintext_refresh_token
if secret_strategy.allows_restoring_secrets?
secret_strategy.restore_secret(self, :refresh_token)
else
@raw_refresh_token
end
end
# We keep a volatile copy of the raw token for initial communication
# The stored refresh_token may be mapped and not available in cleartext.
#
# Some strategies allow restoring stored secrets (e.g. symmetric encryption)
# while hashing strategies do not, so you cannot rely on this value
# returning a present value for persisted tokens.
def plaintext_token
if secret_strategy.allows_restoring_secrets?
secret_strategy.restore_secret(self, :token)
else
@raw_token
end
end
# Revokes token with `:refresh_token` equal to `:previous_refresh_token`
# and clears `:previous_refresh_token` attribute.
#
def revoke_previous_refresh_token!
return if !self.class.refresh_token_revoked_on_use? || previous_refresh_token.blank?
old_refresh_token&.revoke
update_attribute(:previous_refresh_token, "")
end
private
# Searches for Access Token record with `:refresh_token` equal to
# `:previous_refresh_token` value.
#
# @return [Doorkeeper::AccessToken, nil]
# Access Token record or nil if nothing found
#
def old_refresh_token
@old_refresh_token ||= self.class.by_previous_refresh_token(previous_refresh_token)
end
# Generates refresh token with UniqueToken generator.
#
# @return [String] refresh token value
#
def generate_refresh_token
@raw_refresh_token = UniqueToken.generate
secret_strategy.store_secret(self, :refresh_token, @raw_refresh_token)
end
# Generates and sets the token value with the
# configured Generator class (see Doorkeeper.config).
#
# @return [String] generated token value
#
# @raise [Doorkeeper::Errors::UnableToGenerateToken]
# custom class doesn't implement .generate method
# @raise [Doorkeeper::Errors::TokenGeneratorNotFound]
# custom class doesn't exist
#
def generate_token
self.created_at ||= Time.now.utc
@raw_token = token_generator.generate(attributes_for_token_generator)
secret_strategy.store_secret(self, :token, @raw_token)
@raw_token
end
# Set of attributes that would be passed to token generator to
# generate unique token based on them.
#
# @return [Hash] set of attributes
#
def attributes_for_token_generator
{
resource_owner_id: resource_owner_id,
scopes: scopes,
application: application,
expires_in: expires_in,
created_at: created_at,
}.tap do |attributes|
if Doorkeeper.config.polymorphic_resource_owner?
attributes[:resource_owner] = resource_owner
end
end
end
def token_generator
generator_name = Doorkeeper.config.access_token_generator
generator = generator_name.constantize
return generator if generator.respond_to?(:generate)
raise Errors::UnableToGenerateToken, "#{generator} does not respond to `.generate`."
rescue NameError
raise Errors::TokenGeneratorNotFound, "#{generator_name} not found"
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/models/concerns/ 0000755 0000041 0000041 00000000000 14422352653 021727 5 ustar www-data www-data doorkeeper-5.6.6/lib/doorkeeper/models/concerns/accessible.rb 0000644 0000041 0000041 00000000525 14422352653 024353 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Models
module Accessible
# Indicates whether the object is accessible (not expired and not revoked).
#
# @return [Boolean] true if object accessible or false in other case
#
def accessible?
!expired? && !revoked?
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/models/concerns/ownership.rb 0000644 0000041 0000041 00000000577 14422352653 024303 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Models
module Ownership
extend ActiveSupport::Concern
included do
belongs_to :owner, polymorphic: true, optional: true
validates :owner, presence: true, if: :validate_owner?
end
def validate_owner?
Doorkeeper.config.confirm_application_owner?
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/models/concerns/revocable.rb 0000644 0000041 0000041 00000001104 14422352653 024212 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Models
module Revocable
# Revokes the object (updates `:revoked_at` attribute setting its value
# to the specific time).
#
# @param clock [Time] time object
#
def revoke(clock = Time)
update_attribute(:revoked_at, clock.now.utc)
end
# Indicates whether the object has been revoked.
#
# @return [Boolean] true if revoked, false in other case
#
def revoked?
!!(revoked_at && revoked_at <= Time.now.utc)
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/models/concerns/orderable.rb 0000644 0000041 0000041 00000000431 14422352653 024211 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Models
module Orderable
extend ActiveSupport::Concern
module ClassMethods
def ordered_by(attribute, direction = :asc)
order(attribute => direction)
end
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/models/concerns/scopes.rb 0000644 0000041 0000041 00000001144 14422352653 023550 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Models
module Scopes
def scopes
OAuth::Scopes.from_string(scopes_string)
end
def scopes=(value)
if value.is_a?(Array)
super(Doorkeeper::OAuth::Scopes.from_array(value).to_s)
else
super(Doorkeeper::OAuth::Scopes.from_string(value.to_s).to_s)
end
end
def scopes_string
self[:scopes]
end
def includes_scope?(*required_scopes)
required_scopes.blank? || required_scopes.any? { |scope| scopes.exists?(scope.to_s) }
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/models/concerns/expirable.rb 0000644 0000041 0000041 00000001772 14422352653 024236 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Models
module Expirable
# Indicates whether the object is expired (`#expires_in` present and
# expiration time has come).
#
# @return [Boolean] true if object expired and false in other case
def expired?
!!(expires_in && Time.now.utc > expires_at)
end
# Calculates expiration time in seconds.
#
# @return [Integer, nil] number of seconds if object has expiration time
# or nil if object never expires.
def expires_in_seconds
return nil if expires_in.nil?
expires = expires_at - Time.now.utc
expires_sec = expires.seconds.round(0)
expires_sec > 0 ? expires_sec : 0
end
# Expiration time (date time of creation + TTL).
#
# @return [Time, nil] expiration time in UTC
# or nil if the object never expires.
#
def expires_at
expires_in && created_at + expires_in.seconds
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/models/concerns/polymorphic_resource_owner.rb 0000644 0000041 0000041 00000001302 14422352653 027736 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Models
module PolymorphicResourceOwner
module ForAccessGrant
extend ActiveSupport::Concern
included do
if Doorkeeper.config.polymorphic_resource_owner?
belongs_to :resource_owner, polymorphic: true, optional: false
else
validates :resource_owner_id, presence: true
end
end
end
module ForAccessToken
extend ActiveSupport::Concern
included do
if Doorkeeper.config.polymorphic_resource_owner?
belongs_to :resource_owner, polymorphic: true, optional: true
end
end
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/models/concerns/secret_storable.rb 0000644 0000041 0000041 00000006524 14422352653 025443 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Models
##
# Storable finder to provide lookups for input plaintext values which are
# mapped to their stored versions (e.g., hashing, encryption) before lookup.
module SecretStorable
extend ActiveSupport::Concern
delegate :secret_strategy,
:fallback_secret_strategy,
to: :class
# :nodoc
module ClassMethods
# Compare the given plaintext with the secret
#
# @param input [String]
# The plain input to compare.
#
# @param secret [String]
# The secret value to compare with.
#
# @return [Boolean]
# Whether input matches secret as per the secret strategy
#
delegate :secret_matches?, to: :secret_strategy
# Returns an instance of the Doorkeeper::AccessToken with
# specific token value.
#
# @param attr [Symbol]
# The token attribute we're looking with.
#
# @param token [#to_s]
# token value (any object that responds to `#to_s`)
#
# @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
# if there is no record with such token
#
def find_by_plaintext_token(attr, token)
token = token.to_s
find_by(attr => secret_strategy.transform_secret(token)) ||
find_by_fallback_token(attr, token)
end
# Allow looking up previously plain tokens as a fallback
# IFF a fallback strategy has been defined
#
# @param attr [Symbol]
# The token attribute we're looking with.
#
# @param plain_secret [#to_s]
# plain secret value (any object that responds to `#to_s`)
#
# @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
# if there is no record with such token
#
def find_by_fallback_token(attr, plain_secret)
return nil unless fallback_secret_strategy
# Use the previous strategy to look up
stored_token = fallback_secret_strategy.transform_secret(plain_secret)
find_by(attr => stored_token).tap do |resource|
return nil unless resource
upgrade_fallback_value resource, attr, plain_secret
end
end
# Allow implementations in ORMs to replace a plain
# value falling back to to avoid it remaining as plain text.
#
# @param instance
# An instance of this model with a plain value token.
#
# @param attr
# The secret attribute name to upgrade.
#
# @param plain_secret
# The plain secret to upgrade.
#
def upgrade_fallback_value(instance, attr, plain_secret)
upgraded = secret_strategy.store_secret(instance, attr, plain_secret)
instance.update(attr => upgraded)
end
##
# Determines the secret storing transformer
# Unless configured otherwise, uses the plain secret strategy
def secret_strategy
::Doorkeeper::SecretStoring::Plain
end
##
# Determine the fallback storing strategy
# Unless configured, there will be no fallback
def fallback_secret_strategy
nil
end
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/models/concerns/reusable.rb 0000644 0000041 0000041 00000001025 14422352653 024054 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Models
module Reusable
# Indicates whether the object is reusable (i.e. It is not expired and
# has not crossed reuse_limit).
#
# @return [Boolean] true if can be reused and false in other case
def reusable?
return false if expired?
return true unless expires_in
threshold_limit = 100 - Doorkeeper.config.token_reuse_limit
expires_in_seconds >= threshold_limit * expires_in / 100
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/models/concerns/resource_ownerable.rb 0000644 0000041 0000041 00000002543 14422352653 026145 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Models
module ResourceOwnerable
extend ActiveSupport::Concern
module ClassMethods
# Searches for record by Resource Owner considering Doorkeeper
# configuration for resource owner association.
#
# @param resource_owner [ActiveRecord::Base, Integer]
# resource owner
#
# @return [Doorkeeper::AccessGrant, Doorkeeper::AccessToken]
# collection of records
#
def by_resource_owner(resource_owner)
if Doorkeeper.configuration.polymorphic_resource_owner?
where(resource_owner: resource_owner)
else
where(resource_owner_id: resource_owner_id_for(resource_owner))
end
end
protected
# Backward compatible way to retrieve resource owner itself (if
# polymorphic association enabled) or just it's ID.
#
# @param resource_owner [ActiveRecord::Base, Integer]
# resource owner
#
# @return [ActiveRecord::Base, Integer]
# instance of Resource Owner or it's ID
#
def resource_owner_id_for(resource_owner)
if resource_owner.respond_to?(:to_key)
resource_owner.id
else
resource_owner
end
end
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/models/concerns/expiration_time_sql_math.rb 0000644 0000041 0000041 00000005325 14422352653 027351 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Models
module ExpirationTimeSqlMath
extend ::ActiveSupport::Concern
class ExpirationTimeSqlGenerator
attr_reader :model
delegate :table_name, to: :@model
def initialize(model)
@model = model
end
def generate_sql
raise "`generate_sql` should be overridden for a #{self.class.name}!"
end
end
class MySqlExpirationTimeSqlGenerator < ExpirationTimeSqlGenerator
def generate_sql
Arel.sql("DATE_ADD(#{table_name}.created_at, INTERVAL #{table_name}.expires_in SECOND)")
end
end
class SqlLiteExpirationTimeSqlGenerator < ExpirationTimeSqlGenerator
def generate_sql
Arel.sql("DATETIME(#{table_name}.created_at, '+' || #{table_name}.expires_in || ' SECONDS')")
end
end
class SqlServerExpirationTimeSqlGenerator < ExpirationTimeSqlGenerator
def generate_sql
Arel.sql("DATEADD(second, #{table_name}.expires_in, #{table_name}.created_at) AT TIME ZONE 'UTC'")
end
end
class OracleExpirationTimeSqlGenerator < ExpirationTimeSqlGenerator
def generate_sql
Arel.sql("#{table_name}.created_at + INTERVAL to_char(#{table_name}.expires_in) second")
end
end
class PostgresExpirationTimeSqlGenerator < ExpirationTimeSqlGenerator
def generate_sql
Arel.sql("#{table_name}.created_at + #{table_name}.expires_in * INTERVAL '1 SECOND'")
end
end
ADAPTERS_MAPPING = {
"sqlite" => SqlLiteExpirationTimeSqlGenerator,
"sqlite3" => SqlLiteExpirationTimeSqlGenerator,
"postgis" => PostgresExpirationTimeSqlGenerator,
"postgresql" => PostgresExpirationTimeSqlGenerator,
"mysql" => MySqlExpirationTimeSqlGenerator,
"mysql2" => MySqlExpirationTimeSqlGenerator,
"trilogy" => MySqlExpirationTimeSqlGenerator,
"sqlserver" => SqlServerExpirationTimeSqlGenerator,
"oracleenhanced" => OracleExpirationTimeSqlGenerator,
}.freeze
module ClassMethods
def supports_expiration_time_math?
ADAPTERS_MAPPING.key?(adapter_name.downcase) ||
respond_to?(:custom_expiration_time_sql)
end
def expiration_time_sql
if respond_to?(:custom_expiration_time_sql)
custom_expiration_time_sql
else
expiration_time_sql_expression
end
end
def expiration_time_sql_expression
ADAPTERS_MAPPING.fetch(adapter_name.downcase).new(self).generate_sql
end
def adapter_name
ActiveRecord::Base.connection.adapter_name
end
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/models/access_grant_mixin.rb 0000644 0000041 0000041 00000010160 14422352653 024300 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module AccessGrantMixin
extend ActiveSupport::Concern
include OAuth::Helpers
include Models::Expirable
include Models::Revocable
include Models::Accessible
include Models::Orderable
include Models::SecretStorable
include Models::Scopes
include Models::ResourceOwnerable
# Never uses PKCE if PKCE migrations were not generated
def uses_pkce?
self.class.pkce_supported? && code_challenge.present?
end
module ClassMethods
# Searches for Doorkeeper::AccessGrant record with the
# specific token value.
#
# @param token [#to_s] token value (any object that responds to `#to_s`)
#
# @return [Doorkeeper::AccessGrant, nil]
# AccessGrant object or nil if there is no record with such token
#
def by_token(token)
find_by_plaintext_token(:token, token)
end
# Revokes AccessGrant records that have not been revoked and associated
# with the specific Application and Resource Owner.
#
# @param application_id [Integer]
# ID of the Application
# @param resource_owner [ActiveRecord::Base, Integer]
# instance of the Resource Owner model or it's ID
#
def revoke_all_for(application_id, resource_owner, clock = Time)
by_resource_owner(resource_owner)
.where(
application_id: application_id,
revoked_at: nil,
)
.update_all(revoked_at: clock.now.utc)
end
# Implements PKCE code_challenge encoding without base64 padding as described in the spec.
# https://datatracker.ietf.org/doc/html/rfc7636#appendix-A
# Appendix A. Notes on Implementing Base64url Encoding without Padding
#
# This appendix describes how to implement a base64url-encoding
# function without padding, based upon the standard base64-encoding
# function that uses padding.
#
# To be concrete, example C# code implementing these functions is shown
# below. Similar code could be used in other languages.
#
# static string base64urlencode(byte [] arg)
# {
# string s = Convert.ToBase64String(arg); // Regular base64 encoder
# s = s.Split('=')[0]; // Remove any trailing '='s
# s = s.Replace('+', '-'); // 62nd char of encoding
# s = s.Replace('/', '_'); // 63rd char of encoding
# return s;
# }
#
# An example correspondence between unencoded and encoded values
# follows. The octet sequence below encodes into the string below,
# which when decoded, reproduces the octet sequence.
#
# 3 236 255 224 193
#
# A-z_4ME
#
# https://ruby-doc.org/stdlib-2.1.3/libdoc/base64/rdoc/Base64.html#method-i-urlsafe_encode64
#
# urlsafe_encode64(bin)
# Returns the Base64-encoded version of bin. This method complies with
# "Base 64 Encoding with URL and Filename Safe Alphabet" in RFC 4648.
# The alphabet uses '-' instead of '+' and '_' instead of '/'.
# @param code_verifier [#to_s] a one time use value (any object that responds to `#to_s`)
#
# @return [#to_s] An encoded code challenge based on the provided verifier
# suitable for PKCE validation
#
def generate_code_challenge(code_verifier)
Base64.urlsafe_encode64(Digest::SHA256.digest(code_verifier), padding: false)
end
def pkce_supported?
column_names.include?("code_challenge")
end
##
# Determines the secret storing transformer
# Unless configured otherwise, uses the plain secret strategy
#
# @return [Doorkeeper::SecretStoring::Base]
#
def secret_strategy
::Doorkeeper.config.token_secret_strategy
end
##
# Determine the fallback storing strategy
# Unless configured, there will be no fallback
#
# @return [Doorkeeper::SecretStoring::Base]
#
def fallback_secret_strategy
::Doorkeeper.config.token_secret_fallback_strategy
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/models/application_mixin.rb 0000644 0000041 0000041 00000005752 14422352653 024162 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module ApplicationMixin
extend ActiveSupport::Concern
include OAuth::Helpers
include Models::Orderable
include Models::SecretStorable
include Models::Scopes
# :nodoc
module ClassMethods
# Returns an instance of the Doorkeeper::Application with
# specific UID and secret.
#
# Public/Non-confidential applications will only find by uid if secret is
# blank.
#
# @param uid [#to_s] UID (any object that responds to `#to_s`)
# @param secret [#to_s] secret (any object that responds to `#to_s`)
#
# @return [Doorkeeper::Application, nil]
# Application instance or nil if there is no record with such credentials
#
def by_uid_and_secret(uid, secret)
app = by_uid(uid)
return unless app
return app if secret.blank? && !app.confidential?
return unless app.secret_matches?(secret)
app
end
# Returns an instance of the Doorkeeper::Application with specific UID.
#
# @param uid [#to_s] UID (any object that responds to `#to_s`)
#
# @return [Doorkeeper::Application, nil] Application instance or nil
# if there is no record with such UID
#
def by_uid(uid)
find_by(uid: uid.to_s)
end
##
# Determines the secret storing transformer
# Unless configured otherwise, uses the plain secret strategy
def secret_strategy
::Doorkeeper.config.application_secret_strategy
end
##
# Determine the fallback storing strategy
# Unless configured, there will be no fallback
def fallback_secret_strategy
::Doorkeeper.config.application_secret_fallback_strategy
end
end
# Set an application's valid redirect URIs.
#
# @param uris [String, Array] Newline-separated string or array the URI(s)
#
# @return [String] The redirect URI(s) separated by newlines.
#
def redirect_uri=(uris)
super(uris.is_a?(Array) ? uris.join("\n") : uris)
end
# Check whether the given plain text secret matches our stored secret
#
# @param input [#to_s] Plain secret provided by user
# (any object that responds to `#to_s`)
#
# @return [Boolean] Whether the given secret matches the stored secret
# of this application.
#
def secret_matches?(input)
# return false if either is nil, since secure_compare depends on strings
# but Application secrets MAY be nil depending on confidentiality.
return false if input.nil? || secret.nil?
# When matching the secret by comparer function, all is well.
return true if secret_strategy.secret_matches?(input, secret)
# When fallback lookup is enabled, ensure applications
# with plain secrets can still be found
if fallback_secret_strategy
fallback_secret_strategy.secret_matches?(input, secret)
else
false
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/errors.rb 0000644 0000041 0000041 00000002215 14422352653 020473 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Errors
class DoorkeeperError < StandardError
def type
message
end
end
class InvalidGrantReuse < DoorkeeperError
def type
:invalid_grant
end
end
class InvalidTokenStrategy < DoorkeeperError
def type
:unsupported_grant_type
end
end
class MissingRequiredParameter < DoorkeeperError
attr_reader :missing_param
def initialize(missing_param)
super
@missing_param = missing_param
end
def type
:invalid_request
end
end
class BaseResponseError < DoorkeeperError
attr_reader :response
def initialize(response)
@response = response
end
end
UnableToGenerateToken = Class.new(DoorkeeperError)
TokenGeneratorNotFound = Class.new(DoorkeeperError)
NoOrmCleaner = Class.new(DoorkeeperError)
InvalidToken = Class.new(BaseResponseError)
TokenExpired = Class.new(InvalidToken)
TokenRevoked = Class.new(InvalidToken)
TokenUnknown = Class.new(InvalidToken)
TokenForbidden = Class.new(InvalidToken)
end
end
doorkeeper-5.6.6/lib/doorkeeper/request/ 0000755 0000041 0000041 00000000000 14422352653 020322 5 ustar www-data www-data doorkeeper-5.6.6/lib/doorkeeper/request/authorization_code.rb 0000644 0000041 0000041 00000001051 14422352653 024536 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Request
class AuthorizationCode < Strategy
delegate :client, :parameters, to: :server
def request
@request ||= OAuth::AuthorizationCodeRequest.new(
Doorkeeper.config,
grant,
client,
parameters,
)
end
private
def grant
raise Errors::MissingRequiredParameter, :code if parameters[:code].blank?
Doorkeeper.config.access_grant_model.by_token(parameters[:code])
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/request/refresh_token.rb 0000644 0000041 0000041 00000000750 14422352653 023507 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Request
class RefreshToken < Strategy
delegate :credentials, :parameters, to: :server
def refresh_token
Doorkeeper.config.access_token_model.by_refresh_token(parameters[:refresh_token])
end
def request
@request ||= OAuth::RefreshTokenRequest.new(
Doorkeeper.config,
refresh_token,
credentials,
parameters,
)
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/request/strategy.rb 0000644 0000041 0000041 00000000525 14422352653 022513 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Request
class Strategy
attr_reader :server
delegate :authorize, to: :request
def initialize(server)
@server = server
end
def request
raise NotImplementedError, "request strategies must define #request"
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/request/token.rb 0000644 0000041 0000041 00000000524 14422352653 021770 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Request
class Token < Strategy
delegate :current_resource_owner, to: :server
def pre_auth
server.context.send(:pre_auth)
end
def request
@request ||= OAuth::TokenRequest.new(pre_auth, current_resource_owner)
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/request/client_credentials.rb 0000644 0000041 0000041 00000000522 14422352653 024501 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Request
class ClientCredentials < Strategy
delegate :client, :parameters, to: :server
def request
@request ||= OAuth::ClientCredentialsRequest.new(
Doorkeeper.config,
client,
parameters,
)
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/request/code.rb 0000644 0000041 0000041 00000000522 14422352653 021560 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Request
class Code < Strategy
delegate :current_resource_owner, to: :server
def pre_auth
server.context.send(:pre_auth)
end
def request
@request ||= OAuth::CodeRequest.new(pre_auth, current_resource_owner)
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/request/password.rb 0000644 0000041 0000041 00000000633 14422352653 022513 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Request
class Password < Strategy
delegate :credentials, :resource_owner, :parameters, :client, to: :server
def request
@request ||= OAuth::PasswordAccessTokenRequest.new(
Doorkeeper.config,
client,
credentials,
resource_owner,
parameters,
)
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/request.rb 0000644 0000041 0000041 00000004345 14422352653 020655 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Request
class << self
def authorization_strategy(response_type)
grant_flow = authorization_flows.detect do |flow|
flow.matches_response_type?(response_type)
end
if grant_flow
grant_flow.response_type_strategy
else
# [NOTE]: this will be removed in a newer versions of Doorkeeper.
# For retro-compatibility only
build_fallback_strategy_class(response_type)
end
end
def token_strategy(grant_type)
raise Errors::MissingRequiredParameter, :grant_type if grant_type.blank?
grant_flow = token_flows.detect do |flow|
flow.matches_grant_type?(grant_type)
end
if grant_flow
grant_flow.grant_type_strategy
else
# [NOTE]: this will be removed in a newer versions of Doorkeeper.
# For retro-compatibility only
raise Errors::InvalidTokenStrategy unless available.include?(grant_type.to_s)
strategy_class = build_fallback_strategy_class(grant_type)
raise Errors::InvalidTokenStrategy unless strategy_class
strategy_class
end
end
private
def authorization_flows
Doorkeeper.configuration.authorization_response_flows
end
def token_flows
Doorkeeper.configuration.token_grant_flows
end
# [NOTE]: this will be removed in a newer versions of Doorkeeper.
# For retro-compatibility only
def available
Doorkeeper.config.deprecated_token_grant_types_resolver
end
def build_fallback_strategy_class(grant_or_request_type)
strategy_class_name = grant_or_request_type.to_s.tr(" ", "_").camelize
fallback_strategy = "Doorkeeper::Request::#{strategy_class_name}".constantize
::Kernel.warn <<~WARNING
[DOORKEEPER] #{fallback_strategy} found using fallback, it must be
registered using `Doorkeeper::GrantFlow.register(grant_flow_name, **options)`.
This functionality will be removed in a newer versions of Doorkeeper.
WARNING
fallback_strategy
rescue NameError
raise Errors::InvalidTokenStrategy
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/grant_flow/ 0000755 0000041 0000041 00000000000 14422352653 020774 5 ustar www-data www-data doorkeeper-5.6.6/lib/doorkeeper/grant_flow/fallback_flow.rb 0000644 0000041 0000041 00000000344 14422352653 024110 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module GrantFlow
class FallbackFlow < Flow
def handles_grant_type?
false
end
def handles_response_type?
false
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/grant_flow/flow.rb 0000644 0000041 0000041 00000002225 14422352653 022271 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module GrantFlow
class Flow
attr_reader :name, :grant_type_matches, :grant_type_strategy,
:response_type_matches, :response_type_strategy,
:response_mode_matches
def initialize(name, **options)
@name = name
@grant_type_matches = options[:grant_type_matches]
@grant_type_strategy = options[:grant_type_strategy]
@response_type_matches = options[:response_type_matches]
@response_type_strategy = options[:response_type_strategy]
@response_mode_matches = options[:response_mode_matches]
end
def handles_grant_type?
grant_type_matches.present?
end
def handles_response_type?
response_type_matches.present?
end
def matches_grant_type?(value)
grant_type_matches === value
end
def matches_response_type?(value)
response_type_matches === value
end
def default_response_mode
response_mode_matches[0]
end
def matches_response_mode?(value)
response_mode_matches.include?(value)
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/grant_flow/registry.rb 0000644 0000041 0000041 00000002567 14422352653 023203 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module GrantFlow
module Registry
mattr_accessor :flows
self.flows = {}
mattr_accessor :aliases
self.aliases = {}
# Allows to register custom OAuth grant flow so that Doorkeeper
# could recognize and process it.
#
def register(name_or_flow, **options)
unless name_or_flow.is_a?(Doorkeeper::GrantFlow::Flow)
name_or_flow = Flow.new(name_or_flow, **options)
end
flow_key = name_or_flow.name.to_sym
if flows.key?(flow_key)
::Kernel.warn <<~WARNING
[DOORKEEPER] '#{flow_key}' grant flow already registered and will be overridden
in #{caller(1..1).first}
WARNING
end
flows[flow_key] = name_or_flow
end
# Allows to register aliases that could be used in `grant_flows`
# configuration option. It is possible to have aliases like 1:1 or
# 1:N, i.e. "implicit_oidc" => ['token', 'id_token', 'id_token token'].
#
def register_alias(alias_name, **options)
aliases[alias_name.to_sym] = Array.wrap(options.fetch(:as))
end
def expand_alias(alias_name)
aliases.fetch(alias_name.to_sym, [])
end
# [NOTE]: make it to use #fetch after removing fallbacks
def get(name)
flows[name.to_sym]
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/engine.rb 0000644 0000041 0000041 00000002346 14422352653 020431 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
class Engine < Rails::Engine
initializer "doorkeeper.params.filter", after: :load_config_initializers do |app|
if Doorkeeper.configured?
parameters = %w[client_secret authentication_token access_token refresh_token]
parameters << "code" if Doorkeeper.config.grant_flows.include?("authorization_code")
app.config.filter_parameters << /^(#{Regexp.union(parameters)})$/
end
end
initializer "doorkeeper.routes" do
Doorkeeper::Rails::Routes.install!
end
initializer "doorkeeper.helpers" do
ActiveSupport.on_load(:action_controller) do
include Doorkeeper::Rails::Helpers
end
end
config.to_prepare do
Doorkeeper.run_orm_hooks
end
if defined?(Sprockets) && Sprockets::VERSION.chr.to_i >= 4
initializer "doorkeeper.assets.precompile" do |app|
# Force users to use:
# //= link doorkeeper/admin/application.css
# in Doorkeeper 5 for Sprockets 4 instead of precompile.
# Add note to official docs & Wiki
app.config.assets.precompile += %w[
doorkeeper/application.css
doorkeeper/admin/application.css
]
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/stale_records_cleaner.rb 0000644 0000041 0000041 00000001074 14422352653 023503 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
class StaleRecordsCleaner
CLEANER_CLASS = "StaleRecordsCleaner"
def self.for(base_scope)
orm_adapter = "doorkeeper/orm/#{configured_orm}".classify
orm_cleaner = "#{orm_adapter}::#{CLEANER_CLASS}".constantize
orm_cleaner.new(base_scope)
rescue NameError
raise Doorkeeper::Errors::NoOrmCleaner, "'#{configured_orm}' ORM has no cleaner!"
end
def self.new(base_scope)
self.for(base_scope)
end
def self.configured_orm
Doorkeeper.config.orm
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/orm/ 0000755 0000041 0000041 00000000000 14422352653 017427 5 ustar www-data www-data doorkeeper-5.6.6/lib/doorkeeper/orm/active_record.rb 0000644 0000041 0000041 00000003117 14422352653 022567 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
autoload :AccessGrant, "doorkeeper/orm/active_record/access_grant"
autoload :AccessToken, "doorkeeper/orm/active_record/access_token"
autoload :Application, "doorkeeper/orm/active_record/application"
autoload :RedirectUriValidator, "doorkeeper/orm/active_record/redirect_uri_validator"
module Models
autoload :Ownership, "doorkeeper/models/concerns/ownership"
end
# ActiveRecord ORM for Doorkeeper entity models.
# Consists of three main OAuth entities:
# * Access Token
# * Access Grant
# * Application (client)
#
# Do a lazy loading of all the required and configured stuff.
#
module Orm
module ActiveRecord
autoload :StaleRecordsCleaner, "doorkeeper/orm/active_record/stale_records_cleaner"
module Mixins
autoload :AccessGrant, "doorkeeper/orm/active_record/mixins/access_grant"
autoload :AccessToken, "doorkeeper/orm/active_record/mixins/access_token"
autoload :Application, "doorkeeper/orm/active_record/mixins/application"
end
def self.run_hooks
initialize_configured_associations
end
def self.initialize_configured_associations
if Doorkeeper.config.enable_application_owner?
Doorkeeper.config.application_model.include ::Doorkeeper::Models::Ownership
end
Doorkeeper.config.access_grant_model.include ::Doorkeeper::Models::PolymorphicResourceOwner::ForAccessGrant
Doorkeeper.config.access_token_model.include ::Doorkeeper::Models::PolymorphicResourceOwner::ForAccessToken
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/orm/active_record/ 0000755 0000041 0000041 00000000000 14422352653 022240 5 ustar www-data www-data doorkeeper-5.6.6/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb 0000644 0000041 0000041 00000003636 14422352653 027322 0 ustar www-data www-data # frozen_string_literal: true
require "uri"
module Doorkeeper
# ActiveModel validator for redirect URI validation in according
# to OAuth standards and Doorkeeper configuration.
class RedirectUriValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
if value.blank?
return if Doorkeeper.config.allow_blank_redirect_uri?(record)
record.errors.add(attribute, :blank)
else
value.split.each do |val|
next if oob_redirect_uri?(val)
uri = ::URI.parse(val)
record.errors.add(attribute, :forbidden_uri) if forbidden_uri?(uri)
record.errors.add(attribute, :fragment_present) unless uri.fragment.nil?
record.errors.add(attribute, :unspecified_scheme) if unspecified_scheme?(uri)
record.errors.add(attribute, :relative_uri) if relative_uri?(uri)
record.errors.add(attribute, :secured_uri) if invalid_ssl_uri?(uri)
record.errors.add(attribute, :invalid_uri) if unspecified_host?(uri)
end
end
rescue URI::InvalidURIError
record.errors.add(attribute, :invalid_uri)
end
private
def oob_redirect_uri?(uri)
Doorkeeper::OAuth::NonStandard::IETF_WG_OAUTH2_OOB_METHODS.include?(uri)
end
def forbidden_uri?(uri)
Doorkeeper.config.forbid_redirect_uri.call(uri)
end
def unspecified_scheme?(uri)
return true if uri.opaque.present?
%w[localhost].include?(uri.try(:scheme))
end
def unspecified_host?(uri)
uri.is_a?(URI::HTTP) && uri.host.blank?
end
def relative_uri?(uri)
uri.scheme.nil? && uri.host.blank?
end
def invalid_ssl_uri?(uri)
forces_ssl = Doorkeeper.config.force_ssl_in_redirect_uri
non_https = uri.try(:scheme) == "http"
if forces_ssl.respond_to?(:call)
forces_ssl.call(uri) && non_https
else
forces_ssl && non_https
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb 0000644 0000041 0000041 00000001600 14422352653 027104 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Orm
module ActiveRecord
# Helper class to clear stale and non-active tokens and grants.
# Used by Doorkeeper Rake tasks.
#
class StaleRecordsCleaner
def initialize(base_scope)
@base_scope = base_scope
end
# Clears revoked records
def clean_revoked
table = @base_scope.arel_table
@base_scope
.where.not(revoked_at: nil)
.where(table[:revoked_at].lt(Time.current))
.in_batches(&:delete_all)
end
# Clears expired records
def clean_expired(ttl)
table = @base_scope.arel_table
@base_scope
.where.not(expires_in: nil)
.where(table[:created_at].lt(Time.current - ttl))
.in_batches(&:delete_all)
end
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/orm/active_record/access_grant.rb 0000644 0000041 0000041 00000000341 14422352653 025217 0 ustar www-data www-data # frozen_string_literal: true
require "doorkeeper/orm/active_record/mixins/access_grant"
module Doorkeeper
class AccessGrant < ::ActiveRecord::Base
include Doorkeeper::Orm::ActiveRecord::Mixins::AccessGrant
end
end
doorkeeper-5.6.6/lib/doorkeeper/orm/active_record/application.rb 0000644 0000041 0000041 00000000440 14422352653 025066 0 ustar www-data www-data # frozen_string_literal: true
require "doorkeeper/orm/active_record/redirect_uri_validator"
require "doorkeeper/orm/active_record/mixins/application"
module Doorkeeper
class Application < ::ActiveRecord::Base
include ::Doorkeeper::Orm::ActiveRecord::Mixins::Application
end
end
doorkeeper-5.6.6/lib/doorkeeper/orm/active_record/mixins/ 0000755 0000041 0000041 00000000000 14422352653 023547 5 ustar www-data www-data doorkeeper-5.6.6/lib/doorkeeper/orm/active_record/mixins/access_grant.rb 0000644 0000041 0000041 00000003622 14422352653 026533 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper::Orm::ActiveRecord::Mixins
module AccessGrant
extend ActiveSupport::Concern
included do
self.table_name = compute_doorkeeper_table_name
self.strict_loading_by_default = false if respond_to?(:strict_loading_by_default)
include ::Doorkeeper::AccessGrantMixin
belongs_to :application, class_name: Doorkeeper.config.application_class.to_s,
optional: true,
inverse_of: :access_grants
validates :application_id,
:token,
:expires_in,
:redirect_uri,
presence: true
validates :token, uniqueness: { case_sensitive: true }
before_validation :generate_token, on: :create
# We keep a volatile copy of the raw token for initial communication
# The stored refresh_token may be mapped and not available in cleartext.
#
# Some strategies allow restoring stored secrets (e.g. symmetric encryption)
# while hashing strategies do not, so you cannot rely on this value
# returning a present value for persisted tokens.
def plaintext_token
if secret_strategy.allows_restoring_secrets?
secret_strategy.restore_secret(self, :token)
else
@raw_token
end
end
private
# Generates token value with UniqueToken class.
#
# @return [String] token value
#
def generate_token
@raw_token = Doorkeeper::OAuth::Helpers::UniqueToken.generate
secret_strategy.store_secret(self, :token, @raw_token)
end
end
module ClassMethods
private
def compute_doorkeeper_table_name
table_name = "oauth_access_grant"
table_name = table_name.pluralize if pluralize_table_names
"#{table_name_prefix}#{table_name}#{table_name_suffix}"
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/orm/active_record/mixins/application.rb 0000644 0000041 0000041 00000016527 14422352653 026412 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper::Orm::ActiveRecord::Mixins
module Application
extend ActiveSupport::Concern
included do
self.table_name = compute_doorkeeper_table_name
self.strict_loading_by_default = false if respond_to?(:strict_loading_by_default)
include ::Doorkeeper::ApplicationMixin
has_many :access_grants,
foreign_key: :application_id,
dependent: :delete_all,
class_name: Doorkeeper.config.access_grant_class.to_s
has_many :access_tokens,
foreign_key: :application_id,
dependent: :delete_all,
class_name: Doorkeeper.config.access_token_class.to_s
validates :name, :secret, :uid, presence: true
validates :uid, uniqueness: { case_sensitive: true }
validates :redirect_uri, "doorkeeper/redirect_uri": true
validates :confidential, inclusion: { in: [true, false] }
validate :scopes_match_configured, if: :enforce_scopes?
before_validation :generate_uid, :generate_secret, on: :create
has_many :authorized_tokens,
-> { where(revoked_at: nil) },
foreign_key: :application_id,
class_name: Doorkeeper.config.access_token_class.to_s
has_many :authorized_applications,
through: :authorized_tokens,
source: :application
# Generates a new secret for this application, intended to be used
# for rotating the secret or in case of compromise.
#
# @return [String] new transformed secret value
#
def renew_secret
@raw_secret = secret_generator.generate
secret_strategy.store_secret(self, :secret, @raw_secret)
end
# We keep a volatile copy of the raw secret for initial communication
# The stored refresh_token may be mapped and not available in cleartext.
#
# Some strategies allow restoring stored secrets (e.g. symmetric encryption)
# while hashing strategies do not, so you cannot rely on this value
# returning a present value for persisted tokens.
def plaintext_secret
if secret_strategy.allows_restoring_secrets?
secret_strategy.restore_secret(self, :secret)
else
@raw_secret
end
end
# Represents client as set of it's attributes in JSON format.
# This is the right way how we want to override ActiveRecord #to_json.
#
# Respects privacy settings and serializes minimum set of attributes
# for public/private clients and full set for authorized owners.
#
# @return [Hash] entity attributes for JSON
#
def as_json(options = {})
# if application belongs to some owner we need to check if it's the same as
# the one passed in the options or check if we render the client as an owner
if (respond_to?(:owner) && owner && owner == options[:current_resource_owner]) ||
options[:as_owner]
# Owners can see all the client attributes, fallback to ActiveModel serialization
super
else
# if application has no owner or it's owner doesn't match one from the options
# we render only minimum set of attributes that could be exposed to a public
only = extract_serializable_attributes(options)
super(options.merge(only: only))
end
end
def authorized_for_resource_owner?(resource_owner)
Doorkeeper.configuration.authorize_resource_owner_for_client.call(self, resource_owner)
end
# We need to hook into this method to allow serializing plan-text secrets
# when secrets hashing enabled.
#
# @param key [String] attribute name
#
def read_attribute_for_serialization(key)
return super unless key.to_s == "secret"
plaintext_secret || secret
end
private
def secret_generator
generator_name = Doorkeeper.config.application_secret_generator
generator = generator_name.constantize
return generator if generator.respond_to?(:generate)
raise Errors::UnableToGenerateToken, "#{generator} does not respond to `.generate`."
rescue NameError
raise Errors::TokenGeneratorNotFound, "#{generator_name} not found"
end
def generate_uid
self.uid = Doorkeeper::OAuth::Helpers::UniqueToken.generate if uid.blank?
end
def generate_secret
return if secret.present?
renew_secret
end
def scopes_match_configured
if scopes.present? && !Doorkeeper::OAuth::Helpers::ScopeChecker.valid?(
scope_str: scopes.to_s,
server_scopes: Doorkeeper.config.scopes,
)
errors.add(:scopes, :not_match_configured)
end
end
def enforce_scopes?
Doorkeeper.config.enforce_configured_scopes?
end
# Helper method to extract collection of serializable attribute names
# considering serialization options (like `only`, `except` and so on).
#
# @param options [Hash] serialization options
#
# @return [Array]
# collection of attributes to be serialized using #as_json
#
def extract_serializable_attributes(options = {})
opts = options.try(:dup) || {}
only = Array.wrap(opts[:only]).map(&:to_s)
only = if only.blank?
client_serializable_attributes
else
only & client_serializable_attributes
end
only -= Array.wrap(opts[:except]).map(&:to_s) if opts.key?(:except)
only.uniq
end
# Collection of attributes that could be serialized for public.
# Override this method if you need additional attributes to be serialized.
#
# @return [Array] collection of serializable attributes
#
# NOTE: `serializable_attributes` method already taken by Rails >= 6
#
def client_serializable_attributes
attributes = %w[id name created_at]
attributes << "uid" unless confidential?
attributes
end
end
module ClassMethods
# Returns Applications associated with active (not revoked) Access Tokens
# that are owned by the specific Resource Owner.
#
# @param resource_owner [ActiveRecord::Base]
# Resource Owner model instance
#
# @return [ActiveRecord::Relation]
# Applications authorized for the Resource Owner
#
def authorized_for(resource_owner)
resource_access_tokens = Doorkeeper.config.access_token_model.active_for(resource_owner)
where(id: resource_access_tokens.select(:application_id).distinct)
end
# Revokes AccessToken and AccessGrant records that have not been revoked and
# associated with the specific Application and Resource Owner.
#
# @param resource_owner [ActiveRecord::Base]
# instance of the Resource Owner model
#
def revoke_tokens_and_grants_for(id, resource_owner)
Doorkeeper.config.access_token_model.revoke_all_for(id, resource_owner)
Doorkeeper.config.access_grant_model.revoke_all_for(id, resource_owner)
end
private
def compute_doorkeeper_table_name
table_name = "oauth_application"
table_name = table_name.pluralize if pluralize_table_names
"#{table_name_prefix}#{table_name}#{table_name_suffix}"
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/orm/active_record/mixins/access_token.rb 0000644 0000041 0000041 00000005222 14422352653 026536 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper::Orm::ActiveRecord::Mixins
module AccessToken
extend ActiveSupport::Concern
included do
self.table_name = compute_doorkeeper_table_name
self.strict_loading_by_default = false if respond_to?(:strict_loading_by_default)
include ::Doorkeeper::AccessTokenMixin
belongs_to :application, class_name: Doorkeeper.config.application_class.to_s,
inverse_of: :access_tokens,
optional: true
validates :token, presence: true, uniqueness: { case_sensitive: true }
validates :refresh_token, uniqueness: { case_sensitive: true }, if: :use_refresh_token?
# @attr_writer [Boolean, nil] use_refresh_token
# indicates the possibility of using refresh token
attr_writer :use_refresh_token
before_validation :generate_token, on: :create
before_validation :generate_refresh_token,
on: :create, if: :use_refresh_token?
end
module ClassMethods
# Searches for not revoked Access Tokens associated with the
# specific Resource Owner.
#
# @param resource_owner [ActiveRecord::Base]
# Resource Owner model instance
#
# @return [ActiveRecord::Relation]
# active Access Tokens for Resource Owner
#
def active_for(resource_owner)
by_resource_owner(resource_owner).where(revoked_at: nil)
end
def refresh_token_revoked_on_use?
column_names.include?("previous_refresh_token")
end
# Returns non-expired and non-revoked access tokens
def not_expired
relation = where(revoked_at: nil)
if supports_expiration_time_math?
# have not reached the expiration time or it never expires
relation.where("#{expiration_time_sql} > ?", Time.now.utc).or(
relation.where(expires_in: nil)
)
else
::Kernel.warn <<~WARNING.squish
[DOORKEEPER] Doorkeeper doesn't support expiration time math for your database adapter (#{adapter_name}).
Please add a class method `custom_expiration_time_sql` for your AccessToken class/mixin to provide a custom
SQL expression to calculate access token expiration time. See lib/doorkeeper/orm/active_record/mixins/access_token.rb
for more details.
WARNING
relation
end
end
private
def compute_doorkeeper_table_name
table_name = "oauth_access_token"
table_name = table_name.pluralize if pluralize_table_names
"#{table_name_prefix}#{table_name}#{table_name_suffix}"
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/orm/active_record/access_token.rb 0000644 0000041 0000041 00000000341 14422352653 025224 0 ustar www-data www-data # frozen_string_literal: true
require "doorkeeper/orm/active_record/mixins/access_token"
module Doorkeeper
class AccessToken < ::ActiveRecord::Base
include Doorkeeper::Orm::ActiveRecord::Mixins::AccessToken
end
end
doorkeeper-5.6.6/lib/doorkeeper/rails/ 0000755 0000041 0000041 00000000000 14422352653 017744 5 ustar www-data www-data doorkeeper-5.6.6/lib/doorkeeper/rails/routes.rb 0000644 0000041 0000041 00000006165 14422352653 021622 0 ustar www-data www-data # frozen_string_literal: true
require "doorkeeper/rails/routes/mapping"
require "doorkeeper/rails/routes/mapper"
require "doorkeeper/rails/routes/abstract_router"
require "doorkeeper/rails/routes/registry"
module Doorkeeper
module Rails
class Routes # :nodoc:
module Helper
def use_doorkeeper(options = {}, &block)
Doorkeeper::Rails::Routes.new(self, &block).generate_routes!(options)
end
end
include AbstractRouter
extend Registry
mattr_reader :mapping do
{}
end
def self.install!
ActionDispatch::Routing::Mapper.include Doorkeeper::Rails::Routes::Helper
registered_routes.each(&:install!)
end
def initialize(routes, mapper = Mapper.new, &block)
super
end
def generate_routes!(options)
routes.scope options[:scope] || "oauth", as: "oauth" do
map_route(:authorizations, :authorization_routes)
map_route(:tokens, :token_routes)
map_route(:tokens, :revoke_routes)
map_route(:tokens, :introspect_routes) if introspection_routes?
map_route(:applications, :application_routes)
map_route(:authorized_applications, :authorized_applications_routes)
map_route(:token_info, :token_info_routes)
end
end
private
def authorization_routes(mapping)
routes.resource(
:authorization,
path: "authorize",
only: %i[create destroy],
as: mapping[:as],
controller: mapping[:controllers],
) do
routes.get native_authorization_code_route, action: :show, on: :member
routes.get '/', action: :new, on: :member
end
end
def token_routes(mapping)
routes.resource(
:token,
path: "token",
only: [:create], as: mapping[:as],
controller: mapping[:controllers],
)
end
def revoke_routes(mapping)
routes.post "revoke", controller: mapping[:controllers], action: :revoke
end
def introspect_routes(mapping)
routes.post "introspect", controller: mapping[:controllers], action: :introspect
end
def token_info_routes(mapping)
routes.resource(
:token_info,
path: "token/info",
only: [:show], as: mapping[:as],
controller: mapping[:controllers],
)
end
def application_routes(mapping)
routes.resources :doorkeeper_applications,
controller: mapping[:controllers],
as: :applications,
path: "applications"
end
def authorized_applications_routes(mapping)
routes.resources :authorized_applications,
only: %i[index destroy],
controller: mapping[:controllers]
end
def native_authorization_code_route
Doorkeeper.configuration.native_authorization_code_route
end
def introspection_routes?
Doorkeeper.configured? &&
!Doorkeeper.config.allow_token_introspection.is_a?(FalseClass)
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/rails/helpers.rb 0000644 0000041 0000041 00000004320 14422352653 021732 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Rails
module Helpers
def doorkeeper_authorize!(*scopes)
@_doorkeeper_scopes = scopes.presence || Doorkeeper.config.default_scopes
doorkeeper_render_error unless valid_doorkeeper_token?
end
def doorkeeper_unauthorized_render_options(**); end
def doorkeeper_forbidden_render_options(**); end
def valid_doorkeeper_token?
doorkeeper_token&.acceptable?(@_doorkeeper_scopes)
end
private
def doorkeeper_render_error
error = doorkeeper_error
error.raise_exception! if Doorkeeper.config.raise_on_errors?
headers.merge!(error.headers.reject { |k| k == "Content-Type" })
doorkeeper_render_error_with(error)
end
def doorkeeper_render_error_with(error)
options = doorkeeper_render_options(error) || {}
status = doorkeeper_status_for_error(
error, options.delete(:respond_not_found_when_forbidden),
)
if options.blank?
head status
else
options[:status] = status
options[:layout] = false if options[:layout].nil?
render options
end
end
def doorkeeper_error
if doorkeeper_invalid_token_response?
OAuth::InvalidTokenResponse.from_access_token(doorkeeper_token)
else
OAuth::ForbiddenTokenResponse.from_scopes(@_doorkeeper_scopes)
end
end
def doorkeeper_render_options(error)
if doorkeeper_invalid_token_response?
doorkeeper_unauthorized_render_options(error: error)
else
doorkeeper_forbidden_render_options(error: error)
end
end
def doorkeeper_status_for_error(error, respond_not_found_when_forbidden)
if respond_not_found_when_forbidden && error.status == :forbidden
:not_found
else
error.status
end
end
def doorkeeper_invalid_token_response?
!doorkeeper_token || !doorkeeper_token.accessible?
end
def doorkeeper_token
@doorkeeper_token ||= OAuth::Token.authenticate(
request,
*Doorkeeper.config.access_token_methods,
)
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/rails/routes/ 0000755 0000041 0000041 00000000000 14422352653 021265 5 ustar www-data www-data doorkeeper-5.6.6/lib/doorkeeper/rails/routes/abstract_router.rb 0000644 0000041 0000041 00000001412 14422352653 025013 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Rails
# Abstract router module that implements base behavior
# for generating and mapping Rails routes.
#
# Could be reused in Doorkeeper extensions.
#
module AbstractRouter
extend ActiveSupport::Concern
attr_reader :routes
def initialize(routes, mapper = Mapper.new, &block)
@routes = routes
@mapping = mapper.map(&block)
end
def generate_routes!(**_options)
raise NotImplementedError, "must be redefined for #{self.class.name}!"
end
private
def map_route(name, method)
return if @mapping.skipped?(name)
send(method, @mapping[name])
mapping[name] = @mapping[name]
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/rails/routes/mapper.rb 0000644 0000041 0000041 00000001167 14422352653 023103 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Rails
class Routes # :nodoc:
class Mapper
def initialize(mapping = Mapping.new)
@mapping = mapping
end
def map(&block)
instance_eval(&block) if block
@mapping
end
def controllers(controller_names = {})
@mapping.controllers.merge!(controller_names)
end
def skip_controllers(*controller_names)
@mapping.skips = controller_names
end
def as(alias_names = {})
@mapping.as.merge!(alias_names)
end
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/rails/routes/mapping.rb 0000644 0000041 0000041 00000001630 14422352653 023245 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Rails
class Routes # :nodoc:
class Mapping
attr_accessor :controllers, :as, :skips
def initialize
@controllers = {
authorizations: "doorkeeper/authorizations",
applications: "doorkeeper/applications",
authorized_applications: "doorkeeper/authorized_applications",
tokens: "doorkeeper/tokens",
token_info: "doorkeeper/token_info",
}
@as = {
authorizations: :authorization,
tokens: :token,
token_info: :token_info,
}
@skips = []
end
def [](routes)
{
controllers: @controllers[routes],
as: @as[routes],
}
end
def skipped?(controller)
@skips.include?(controller)
end
end
end
end
end
doorkeeper-5.6.6/lib/doorkeeper/rails/routes/registry.rb 0000644 0000041 0000041 00000002340 14422352653 023461 0 ustar www-data www-data # frozen_string_literal: true
module Doorkeeper
module Rails
class Routes
# Thread-safe registry of any Doorkeeper additional routes.
# Used to allow implementing of Doorkeeper extensions that must
# use their own routes.
#
module Registry
ROUTES_ACCESS_LOCK = Mutex.new
ROUTES_DEFINITION_LOCK = Mutex.new
InvalidRouterClass = Class.new(StandardError)
# Collection of additional registered routes for Doorkeeper.
#
# @return [Array