pax_global_header 0000666 0000000 0000000 00000000064 14431346656 0014526 g ustar 00root root 0000000 0000000 52 comment=5987683ccc22262beb6e44c76ca4b65288d6067a doorkeeper-openid_connect-1.8.7/ 0000775 0000000 0000000 00000000000 14431346656 0016667 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/.github/ 0000775 0000000 0000000 00000000000 14431346656 0020227 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/.github/dependabot.yml 0000664 0000000 0000000 00000000305 14431346656 0023055 0 ustar 00root root 0000000 0000000 version: 2 updates: - package-ecosystem: bundler directory: "/" schedule: interval: daily open-pull-requests-limit: 10 ignore: - dependency-name: doorkeeper versions: - 5.5.0 doorkeeper-openid_connect-1.8.7/.github/workflows/ 0000775 0000000 0000000 00000000000 14431346656 0022264 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/.github/workflows/ci.yml 0000664 0000000 0000000 00000001772 14431346656 0023411 0 ustar 00root root 0000000 0000000 name: CI on: [push, pull_request] permissions: contents: read jobs: build: name: >- Ruby ${{ matrix.ruby }} (${{ matrix.gemfile }}) env: CI: true runs-on: ${{ matrix.os }} if: | !( contains(github.event.pull_request.title, '[ci skip]') || contains(github.event.pull_request.title, '[skip ci]')) strategy: fail-fast: true matrix: os: [ ubuntu-latest ] ruby: - 2.7 - '3.0' - '3.1' - '3.2' - head gemfile: - gemfiles/rails_6.0.gemfile - gemfiles/rails_6.1.gemfile - gemfiles/rails_7.0.gemfile - gemfiles/doorkeeper_master.gemfile steps: - name: Repo checkout uses: actions/checkout@v3 - name: Setup Ruby uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} bundler-cache: true - name: Run tests timeout-minutes: 10 run: bundle exec rake spec doorkeeper-openid_connect-1.8.7/.gitignore 0000664 0000000 0000000 00000000271 14431346656 0020657 0 ustar 00root root 0000000 0000000 /.bundle /Gemfile.lock /spec/dummy/db/*.sqlite3* /spec/dummy/db/migrate/*doorkeeper_openid_connect* /spec/dummy/log/*.log /spec/dummy/tmp/ /spec/examples.txt /pkg gemfiles/*.lock *.gem doorkeeper-openid_connect-1.8.7/.rubocop.yml 0000664 0000000 0000000 00000002533 14431346656 0021144 0 ustar 00root root 0000000 0000000 inherit_from: .rubocop_todo.yml require: - rubocop-performance - rubocop-rails - rubocop-rspec AllCops: TargetRubyVersion: 2.7 Exclude: - "spec/dummy/bin/*" - "spec/dummy/db/*" - "spec/dummy/db/*/**" - "spec/dummy/config/*" Layout/TrailingEmptyLines: Enabled: true Layout/DotPosition: EnforcedStyle: leading Layout/LineLength: Exclude: - spec/**/* Max: 100 Metrics/BlockLength: Exclude: - spec/**/* - doorkeeper-openid_connect.gemspec Metrics/MethodLength: Exclude: - spec/dummy/db/**/* Style/FrozenStringLiteralComment: Enabled: true Style/SymbolArray: MinSize: 3 Style/WordArray: MinSize: 3 Style/ClassAndModuleChildren: Enabled: false Style/NumericPredicate: Enabled: false Style/DoubleNegation: Enabled: false Style/HashEachMethods: Enabled: true Style/HashTransformKeys: Enabled: true Style/HashTransformValues: Enabled: true Rails/HttpPositionalArguments: Exclude: - spec/grape/* Rails/HttpStatus: Enabled: false Rails/RakeEnvironment: Exclude: - Rakefile Rails/SkipsModelValidations: Enabled: false Rails/ApplicationRecord: Enabled: false RSpec/DescribeClass: Enabled: false RSpec/ExampleLength: Enabled: false RSpec/FilePath: Enabled: false RSpec/MultipleExpectations: Enabled: false RSpec/NestedGroups: Enabled: false Capybara/FeatureMethods: Enabled: false doorkeeper-openid_connect-1.8.7/.rubocop_todo.yml 0000664 0000000 0000000 00000002325 14431346656 0022170 0 ustar 00root root 0000000 0000000 # This configuration was generated by # `rubocop --auto-gen-config` # on 2020-05-12 19:53:39 +0300 using RuboCop version 0.82.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. Layout/HashAlignment: Enabled: false Layout/FirstHashElementIndentation: Enabled: false Layout/ArgumentAlignment: Enabled: false # Configuration parameters: CountComments, ExcludedMethods. # ExcludedMethods: refine Metrics/BlockLength: Max: 61 # Configuration parameters: CountComments. Metrics/ClassLength: Max: 230 # Configuration parameters: CountComments. Metrics/ModuleLength: Max: 209 Metrics/MethodLength: Max: 15 Style/TrailingCommaInArguments: Enabled: false Style/TrailingCommaInArrayLiteral: Enabled: false Style/TrailingCommaInHashLiteral: Enabled: false # Configuration parameters: . # SupportedStyles: have_received, receive RSpec/MessageSpies: Enabled: false RSpec/NamedSubject: Enabled: false # Configuration parameters: IgnoreNameless, IgnoreSymbolicNames. RSpec/VerifiedDoubles: Enabled: false doorkeeper-openid_connect-1.8.7/.ruby-gemset 0000664 0000000 0000000 00000000032 14431346656 0021126 0 ustar 00root root 0000000 0000000 doorkeeper-openid_connect doorkeeper-openid_connect-1.8.7/CHANGELOG.md 0000664 0000000 0000000 00000023022 14431346656 0020477 0 ustar 00root root 0000000 0000000 ## Unreleased - [#PR ID] Add your changelog entry here. ## v1.8.7 (2023-05-18) - [#198] Fully qualify `JWT::JWK::Thumbprint` constant with :: (thanks to @stanhu) ## v1.8.6 (2023-05-12) - [#194] Default to RFC 7638 kid fingerprint generation (thanks to @stanhu). ## v1.8.5 (2023-02-02) - [#186] Simplify gem configuration reusing Doorkeeper configuration option DSL (thanks to @nbulaj). - [#182] Drop support for Ruby 2.6 and Rails 5 (thanks to @sato11). - [#188] Fix dookeeper-jwt compatibility (thanks to @zavan). ## v1.8.4 (2023-02-01) Note that v1.8.4 changed the default kid fingerprint generation from RFC 7638 to a format based on the SHA256 digest of the key element. To restore the previous behavior, upgrade to v1.8.6. - [#177] Replace `json-jwt` with `ruby-jwt` to align with doorkeeper-jwt (thanks to @kristof-mattei). - [#185] Don't call active_record_options for Doorkeeper >= 5.6.3 (thanks to @zavan). - [#183] Stop render consent screen when user is not logged-in (thanks to @nov). ## v1.8.3 (2022-12-02) - [#180] Add PKCE support to OpenID discovery endpoint (thanks to @stanhu). ## v1.8.2 (2022-07-13) - [#168] Allow to use custom doorkeeper access grant model (thanks @nov). - [#170] Controllers inherit `Doorkeeper::AppliactionMetalController` (thanks @sato11). - [#171] Correctly override `AuthorizationsController` params (thanks to @nbulaj). ## v1.8.1 (2022-02-09) - [#153] Fix ArgumentError caused by client credential validation introduced in Doorkeeper 5.5.1 (thanks to @CircumnavigatingFlatEarther) - [#161] Fix .well-known/openid-connect issuer (respond to block if provided) (thanks to @fkowal). - [#152] Expose oauth-authorization-server in routes (thanks to @mitar) ## v1.8.0 (2021-05-11) No changes from v1.8.0-rc1. ## v1.8.0-rc1 (2021-04-20) ### Upgrading This gem now requires Doorkeeper 5.5 and Ruby 2.5. ### Changes - [#138] Support form_post response mode (thanks to @linhdangduy) - [#144] Support block syntax for `issuer` configuration (thanks to @maxxsnake) - [#145] Register token flows with the strategy instead of the token class (thanks to @paukul) ## v1.7.5 (2020-12-15) ### Changes - [#126] Add discovery_url_options option for discovery endpoints URL generation (thanks to @phlegx) ### Bugfixes - [#123] Remove reference to ApplicationRecord (thanks to @wheeyls) - [#124] Clone doorkeeper.grant_flows array before appending 'refresh_token' (thanks to @davidbasalla) - [#129] Avoid to use the config alias while supporting Doorkeeper 5.2 (thanks to @kymmt90) ## v1.7.4 (2020-07-06) - [#119] Execute end_session_endpoint in the controllers context (thanks to @joeljunstrom) ## v1.7.3 (2020-07-06) - [#111] Add configuration callback `select_account_for_resource_owner` to support the `prompt=select_account` param - [#112] Add grant_types_supported to discovery response - [#114] Fix user_info endpoint when used in api mode - [#116] Support Doorkeeper API (> 5.4) for registering custom grant flows. - [#117] Fix migration template to use Rails migrations DSL for association. - [#118] Use fragment urls for implicit flow error redirects (thanks to @joeljunstrom) ## v1.7.2 (2020-05-20) ### Changes - [#108] Add support for Doorkeeper 5.4 - [#103] Add support for end_session_endpoint - [#109] Test against Ruby 2.7 & Rails 6.x ## v1.7.1 (2020-02-07) ### Upgrading This version adds `on_delete: :cascade` to the migration template for the `oauth_openid_requests` table, in order to fix #82. For existing installations, you should add a new migration in your application to drop the existing foreign key and replace it with a new one with `on_delete: :cascade` included. Depending on the database you're using and the size of your application this might bring up some concerns, but in most cases the following should be sufficient: ```ruby class UpdateOauthOpenIdRequestsForeignKeys < ActiveRecord::Migration[5.2] def up remove_foreign_key(:oauth_openid_requests, column: :access_grant_id) add_foreign_key(:oauth_openid_requests, :oauth_access_grants, column: :access_grant_id, on_delete: :cascade) end def down remove_foreign_key(:oauth_openid_requests, column: :access_grant_id) add_foreign_key(:oauth_openid_requests, :oauth_access_grants, column: :access_grant_id) end end ``` ### Bugfixes - [#96] Bump `json-jwt` because of CVE-2019-18848 (thanks to @leleabhinav) - [#97] Fixes for compatibility with Doorkeeper 5.2 (thanks to @linhdangduy) - [#98] Cascade deletes from `oauth_openid_requests` to `oauth_access_grants` (thanks to @manojmj92) - [#99] Fix `audience` claim when application is not set on access token (thanks to @ionut998) ## v1.7.0 (2019-11-04) ### Changes - [#85] This gem now requires Doorkeeper 5.2, Rails 5, and Ruby 2.4 ## v1.6.3 (2019-09-24) ### Changes - [#81] Allow silent authentication without user consent (thanks to @jarosan) - Don't support Doorkeeper >= 5.2 due to breaking changes ## v1.6.2 (2019-08-09) ### Bugfixes - [#80] Check for client presence in controller, fixes a 500 error when `client_id` is missing (thanks to @cincospenguinos @urnf @isabellechalhoub) ## v1.6.1 (2019-06-07) ### Bugfixes - [#75] Fix return value for `after_successful_response` (thanks to @daveed) ### Changes - [#72] Add `revocation_endpoint` and `introspection_endpoint` to discovery response (thanks to @scarfacedeb) ## v1.6.0 (2019-03-06) ### Changes - [#70] This gem now requires Doorkeeper 5.0, and actually has done so since v1.5.4 (thanks to @michaelglass) ## v1.5.5 (2019-03-03) - [#69] Return `crv` parameter for EC keys (thanks to @marco-nicola) ## v1.5.4 (2019-02-15) ### Bugfixes - [#66] Fix an open redirect vulnerability ([CVE-2019-9837](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9837), thanks to @meagar) - [#67] Don't delete existing tokens with `prompt=consent` (thanks to @nov) ### Changes - [#62] Support customization of redirect params in `id_token` and `id_token token` responses (thanks to @meagar) ## v1.5.3 (2019-01-19) ### Bugfixes - [#60] Don't break native authorization in Doorkeeper 5.x ### Changes - [#58] Use versioned migrations for Rails 5.x (thanks to @tvongaza) ## v1.5.2 (2018-09-04) ### Changes - [#56] The previous release was a bit premature, this fixes some compatibility issues with Doorkeeper 5.x ## v1.5.1 (2018-09-04) ### Changes - [#55] This gem is now compatible with Doorkeeper 5.x ## v1.5.0 (2018-06-27) ### Features - [#52] Custom claims can now also be returned directly in the ID token, see the updated README for usage instructions ## v1.4.0 (2018-05-31) ### Upgrading - Support for Ruby versions older than 2.3 was dropped ### Features - Redirect errors per Section 3.1.2.6 of OpenID Connect 1.0 (by @ryands) - Set `id_token` when it's nil in token response (it's used in `refresh_token` requests) (by @Miouge1) ## v1.3.0 (2018-03-05) ### Features - Support for Implicit Flow (`response_type=id_token` and `response_type=id_token token`), see the updated README for usage instructions (by @nashby, @nhance and @stevenvegt) ## v1.2.0 (2017-08-31) ### Upgrading - The configuration setting `jws_private_key` was renamed to `signing_key`, you can still use the old name until it's removed in the next major release ### Features - Support for pairwise subject identifiers (by @travisofthenorth) - Support for EC and HMAC signing algorithms (by @110y) - Claims now receive an optional third `access_token` argument which allow you to dynamically adjust claim values based on the client's token (by @gigr) ### Bugfixes ## v1.1.2 (2017-01-18) ### Bugfixes - Fixes the `undefined local variable or method 'pre_auth'` error ## v1.1.1 (2017-01-18) #### Upgrading - The configuration setting `jws_public_key` wasn't actually used, it's deprecated now and will be removed in the next major release - The undocumented shorthand `to_proc` syntax for defining claims (`claim :user, &:name`) is not supported anymore #### Features - Claims now receive an optional second `scopes` argument which allow you to dynamically adjust claim values based on the requesting applications' scopes (by @nbibler) - The `prompt` parameter values `login` and `consent` are now supported - The configuration setting `protocol` was added (by @gigr) #### Bugfixes - Standard Claims are now mapped correctly to their default scopes (by @tylerhunt) - Blank `nonce` parameters are now ignored #### Changes - `nil` values and empty strings are now removed from the UserInfo and IdToken responses - Allow `json-jwt` dependency at ~> 1.6. (by @nbibler) - Configuration blocks no longer internally use `instance_eval` which previously gave undocumented and unexpected `self` access to the caller (by @nbibler) ## v1.1.0 (2016-11-30) This release is a general clean-up and adds support for some advanced OpenID Connect features. #### Upgrading - This version adds a table to store temporary nonces, use the generator `doorkeeper:openid_connect:migration` to create a migration - Implement the new configuration callbacks `auth_time_from_resource_owner` and `reauthenticate_resource_owner` to support advanced features #### Features - Add discovery endpoint ([a16caa8](/../../commit/a16caa8)) - Add webfinger and keys endpoints for discovery ([f70898b](/../../commit/f70898b)) - Add supported claims to discovery response ([1d8f9ea](/../../commit/1d8f9ea)) - Support prompt=none parameter ([c775d8b](/../../commit/c775d8b)) - Store and return nonces in IdToken responses ([d28ca8c](/../../commit/d28ca8c)) - Add generator for initializer ([80399fd](/../../commit/80399fd)) - Support max_age parameter ([aabe3aa](/../../commit/aabe3aa)) - Respect scope grants in UserInfo response ([25f2170](/../../commit/25f2170)) doorkeeper-openid_connect-1.8.7/CONTRIBUTING.md 0000664 0000000 0000000 00000003411 14431346656 0021117 0 ustar 00root root 0000000 0000000 # Contributing ## Workflow We are using the [Feature Branch Workflow (also known as GitHub Flow)](https://guides.github.com/introduction/flow/), and prefer delivery as pull requests. Our first line of defense is the [Travis CI](https://travis-ci.org/doorkeeper-gem/doorkeeper-openid_connect) build defined within [.travis.yml](.travis.yml) and triggered for every pull request. Create a feature branch: ```sh git checkout -B feature/contributing ``` ## Creating Good Commits The cardinal rule for creating good commits is to ensure there is only one "logical change" per commit. Why is this an important rule? * The smaller the amount of code being changed, the quicker & easier it is to review & identify potential flaws. * If a change is found to be flawed later, it may be necessary to revert the broken commit. This is much easier to do if there are not other unrelated code changes entangled with the original commit. * When troubleshooting problems using Git's bisect capability, small well defined changes will aid in isolating exactly where the code problem was introduced. * When browsing history using Git annotate/blame, small well defined changes also aid in isolating exactly where & why a piece of code came from. Things to avoid when creating commits: * Mixing whitespace changes with functional code changes. * Mixing two unrelated functional changes. * Sending large new features in a single giant commit. ## Release process - Bump version in `lib/doorkeeper/openid_connect/version.rb`. - Update `CHANGELOG.md`. - Commit all changes. - Tag release and publish gem with `rake release`. - [Publish a new release on GitHub](https://github.com/doorkeeper-gem/doorkeeper-openid_connect/releases/new), using the created tag and the new entries in `CHANGELOG.md`. doorkeeper-openid_connect-1.8.7/Gemfile 0000664 0000000 0000000 00000000531 14431346656 0020161 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true source 'https://rubygems.org' # use Rails version specified by environment ENV['rails'] ||= '6.0.0' gem 'rails', "~> #{ENV['rails']}" gem 'rails-controller-testing' gem 'rubocop', '~> 1.6' gem 'rubocop-performance', require: false gem 'rubocop-rails', require: false gem 'rubocop-rspec', require: false gemspec doorkeeper-openid_connect-1.8.7/LICENSE.txt 0000664 0000000 0000000 00000002057 14431346656 0020516 0 ustar 00root root 0000000 0000000 MIT License Copyright (c) 2014 PlayOn! Sports Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. doorkeeper-openid_connect-1.8.7/README.md 0000664 0000000 0000000 00000036115 14431346656 0020154 0 ustar 00root root 0000000 0000000 # Doorkeeper::OpenidConnect [](https://github.com/doorkeeper-gem/doorkeeper-openid_connect/actions) [](https://codeclimate.com/github/doorkeeper-gem/doorkeeper-openid_connect) [](https://rubygems.org/gems/doorkeeper-openid_connect) #### :warning: **This project is looking for maintainers, see [this issue](https://github.com/doorkeeper-gem/doorkeeper-openid_connect/issues/89).** This library implements an [OpenID Connect](http://openid.net/connect/) authentication provider for Rails applications on top of the [Doorkeeper](https://github.com/doorkeeper-gem/doorkeeper) OAuth 2.0 framework. OpenID Connect is a single-sign-on and identity layer with a [growing list of server and client implementations](http://openid.net/developers/libraries/). If you're looking for a client in Ruby check out [omniauth_openid_connect](https://github.com/m0n9oose/omniauth_openid_connect/). ## Table of Contents - [Status](#status) - [Known Issues](#known-issues) - [Example Applications](#example-applications) - [Installation](#installation) - [Configuration](#configuration) - [Scopes](#scopes) - [Claims](#claims) - [Routes](#routes) - [Nonces](#nonces) - [Internationalization (I18n)](#internationalization-i18n) - [Development](#development) - [License](#license) - [Sponsors](#sponsors) ## Status The following parts of [OpenID Connect Core 1.0](http://openid.net/specs/openid-connect-core-1_0.html) are currently supported: - [Authentication using the Authorization Code Flow](http://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth) - [Authentication using the Implicit Flow](http://openid.net/specs/openid-connect-core-1_0.html#ImplicitFlowAuth) - [Requesting Claims using Scope Values](http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims) - [UserInfo Endpoint](http://openid.net/specs/openid-connect-core-1_0.html#UserInfo) - [Normal Claims](http://openid.net/specs/openid-connect-core-1_0.html#NormalClaims) - [OAuth 2.0 Form Post Response Mode](https://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html) In addition we also support most of [OpenID Connect Discovery 1.0](http://openid.net/specs/openid-connect-discovery-1_0.html) for automatic configuration discovery. Take a look at the [DiscoveryController](app/controllers/doorkeeper/openid_connect/discovery_controller.rb) for more details on supported features. ### Known Issues - Doorkeeper's API mode (`Doorkeeper.configuration.api_only`) is not properly supported yet ### Example Applications - [GitLab](https://gitlab.com/gitlab-org/gitlab-ce) ([original MR](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8018)) - [Testing app for this gem](https://github.com/doorkeeper-gem/doorkeeper-openid_connect/tree/master/spec/dummy) ## Installation Make sure your application is already set up with [Doorkeeper](https://github.com/doorkeeper-gem/doorkeeper#installation). Add this line to your application's `Gemfile` and run `bundle install`: ```ruby gem 'doorkeeper-openid_connect' ``` Run the installation generator to update routes and create the initializer: ```sh rails generate doorkeeper:openid_connect:install ``` Generate a migration for Active Record (other ORMs are currently not supported): ```sh rails generate doorkeeper:openid_connect:migration rake db:migrate ``` If you're upgrading from an earlier version, check [CHANGELOG.md](CHANGELOG.md) for upgrade instructions. ## Configuration Make sure you've [configured Doorkeeper](https://github.com/doorkeeper-gem/doorkeeper#configuration) before continuing. Verify your settings in `config/initializers/doorkeeper.rb`: - `resource_owner_authenticator` - This callback needs to returns a falsey value if the current user can't be determined: ```ruby resource_owner_authenticator do if current_user current_user else redirect_to(new_user_session_url) nil end end ``` - `grant_flows` - If you want to use `id_token` or `id_token token` response types you need to add `implicit_oidc` to `grant_flows`: ```ruby grant_flows %w(authorization_code implicit_oidc) ``` The following settings are required in `config/initializers/doorkeeper_openid_connect.rb`: - `issuer` - Identifier for the issuer of the response (i.e. your application URL). The value is a case sensitive URL using the `https` scheme that contains scheme, host, and optionally, port number and path components and no query or fragment components. - You can either pass a string value, or a block to generate the issuer dynamically based on the `resource_owner` and `application` or [request](app/controllers/doorkeeper/openid_connect/discovery_controller.rb#L123) passed to the block. - `subject` - Identifier for the resource owner (i.e. the authenticated user). A locally unique and never reassigned identifier within the issuer for the end-user, which is intended to be consumed by the client. The value is a case-sensitive string and must not exceed 255 ASCII characters in length. - The database ID of the user is an acceptable choice if you don't mind leaking that information. - If you want to provide a different subject identifier to each client, use [pairwise subject identifier](http://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes) with configurations like below. ```ruby # config/initializers/doorkeeper_openid_connect.rb Doorkeeper::OpenidConnect.configure do # ... subject_types_supported [:pairwise] subject do |resource_owner, application| Digest::SHA256.hexdigest("#{resource_owner.id}#{URI.parse(application.redirect_uri).host}#{'your_secret_salt'}") end # ... end ``` - `signing_key` - Private key to be used for [JSON Web Signature](https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-31). - You can generate a private key with the `openssl` command, see e.g. [Generate an RSA keypair using OpenSSL](https://en.wikibooks.org/wiki/Cryptography/Generate_a_keypair_using_OpenSSL). - You should not commit the key to your repository, but use an external file (in combination with `File.read`) and/or the [dotenv-rails](https://github.com/bkeepers/dotenv) gem (in combination with `ENV[...]`). - `signing_algorithm` - The encryption type of the private key which defaults to `:rs256`. The list of supported algorithms can be found [here](https://github.com/nov/json-jwt/wiki/JWE#supported-algorithms) - `resource_owner_from_access_token` - Defines how to translate the Doorkeeper access token to a resource owner model. The following settings are optional, but recommended for better client compatibility: - `auth_time_from_resource_owner` - Returns the time of the user's last login, this can be a `Time`, `DateTime`, or any other class that responds to `to_i` - Required to support the `max_age` parameter and the `auth_time` claim. - `reauthenticate_resource_owner` - Defines how to trigger reauthentication for the current user (e.g. display a password prompt, or sign-out the user and redirect to the login form). - Required to support the `max_age` and `prompt=login` parameters. - The block is executed in the controller's scope, so you have access to methods like `params`, `redirect_to` etc. - `select_account_for_resource_owner` - Defines how to trigger account selection to choose the current login user. - Required to support the `prompt=select_account` parameter. - The block is executed in the controller's scope, so you have access to methods like `params`, `redirect_to` etc. The following settings are optional: - `expiration` - Expiration time after which the ID Token must not be accepted for processing by clients. - The default is 120 seconds - `protocol` - The protocol to use when generating URIs for the discovery endpoints. - The default is `https` for production, and `http` for all other environments - Note that the OIDC specification mandates HTTPS, so you shouldn't change this for production environments unless you have a really good reason! - `end_session_endpoint` - The URL that the user is redirected to after ending the session on the client. - Used by implementations like https://github.com/IdentityModel/oidc-client-js. - The block is executed in the controller's scope, so you have access to your route helpers. - `discovery_url_options` - The URL options for every available endpoint to use when generating the endpoint URL in the discovery response. Available endpoints: `authorization`, `token`, `revocation`, `introspection`, `userinfo`, `jwks`, `webfinger`. - This option requires option keys with an available endpoint and [URL options](https://api.rubyonrails.org/v6.0.3.3/classes/ActionDispatch/Routing/UrlFor.html#method-i-url_for) as value. - The default is to use the request host, just like all the other URLs in the discovery response. - This is useful when you want endpoints to use a different URL than other requests. For example, if your Doorkeeper server is behind a firewall with other servers, you might want other servers to use an "internal" URL to communicate with Doorkeeper, but you want to present an "external" URL to end-users for authentication requests. Note that this setting does not actually change the URL that your Doorkeeper server responds on - that is outside the scope of Doorkeeper. ```ruby # config/initializers/doorkeeper_openid_connect.rb Doorkeeper::OpenidConnect.configure do # ... discovery_url_options do |request| { authorization: { host: 'host.example.com' }, jwks: { protocol: request.ssl? ? :https : :http } } end # ... end ``` ### Scopes To perform authentication over OpenID Connect, an OAuth client needs to request the `openid` scope. This scope needs to be enabled using either `optional_scopes` in the global Doorkeeper configuration in `config/initializers/doorkeeper.rb`, or by adding it to any OAuth application's `scope` attribute. > Note that any application defining its own scopes won't inherit the scopes defined in the initializer, so you might have to update existing applications as well. > > See [Using Scopes](https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Scopes) in the Doorkeeper wiki for more information. ### Claims Claims can be defined in a `claims` block inside `config/initializers/doorkeeper_openid_connect.rb`: ```ruby Doorkeeper::OpenidConnect.configure do claims do claim :email do |resource_owner| resource_owner.email end claim :full_name do |resource_owner| "#{resource_owner.first_name} #{resource_owner.last_name}" end claim :preferred_username, scope: :openid do |resource_owner, scopes, access_token| # Pass the resource_owner's preferred_username if the application has # `profile` scope access. Otherwise, provide a more generic alternative. scopes.exists?(:profile) ? resource_owner.preferred_username : "summer-sun-9449" end claim :groups, response: [:id_token, :user_info] do |resource_owner| resource_owner.groups end end end ``` Each claim block will be passed: - the `resource_owner`, which is the return value of `resource_owner_authenticator` in your initializer - the `scopes` granted by the access token, which is an instance of `Doorkeeper::OAuth::Scopes` - the `access_token` itself, which is an instance of `Doorkeeper::AccessToken` By default all custom claims are only returned from the `UserInfo` endpoint and not included in the ID token. You can optionally pass a `response:` keyword with one or both of the symbols `:id_token` or `:user_info` to specify where the claim should be returned. You can also pass a `scope:` keyword argument on each claim to specify which OAuth scope should be required to access the claim. If you define any of the defined [Standard Claims](http://openid.net/specs/openid-connect-core-1_0.html#StandardClaims) they will by default use their [corresponding scopes](http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims) (`profile`, `email`, `address` and `phone`), and any other claims will by default use the `profile` scope. Again, to use any of these scopes you need to enable them as described above. ### Routes The installation generator will update your `config/routes.rb` to define all required routes: ``` ruby Rails.application.routes.draw do use_doorkeeper_openid_connect # your routes end ``` This will mount the following routes: ``` GET /oauth/userinfo POST /oauth/userinfo GET /oauth/discovery/keys GET /.well-known/openid-configuration GET /.well-known/webfinger ``` With the exception of the hard-coded `/.well-known` paths (see [RFC 5785](https://tools.ietf.org/html/rfc5785)) you can customize routes in the same way as with Doorkeeper, please refer to [this page on their wiki](https://github.com/doorkeeper-gem/doorkeeper/wiki/Customizing-routes#version--05-1). ### Nonces To support clients who send nonces you have to tweak Doorkeeper's authorization view so the parameter is passed on. If you don't already have custom templates, run this generator in your Rails application to add them: ```sh rails generate doorkeeper:views ``` Then tweak the template as follows: ```patch --- i/app/views/doorkeeper/authorizations/new.html.erb +++ w/app/views/doorkeeper/authorizations/new.html.erb @@ -26,6 +26,7 @@ <%= hidden_field_tag :state, @pre_auth.state %> <%= hidden_field_tag :response_type, @pre_auth.response_type %> <%= hidden_field_tag :scope, @pre_auth.scope %> + <%= hidden_field_tag :nonce, @pre_auth.nonce %> <%= submit_tag t('doorkeeper.authorizations.buttons.authorize'), class: "btn btn-success btn-lg btn-block" %> <% end %> <%= form_tag oauth_authorization_path, method: :delete do %> @@ -34,6 +35,7 @@ <%= hidden_field_tag :state, @pre_auth.state %> <%= hidden_field_tag :response_type, @pre_auth.response_type %> <%= hidden_field_tag :scope, @pre_auth.scope %> + <%= hidden_field_tag :nonce, @pre_auth.nonce %> <%= submit_tag t('doorkeeper.authorizations.buttons.deny'), class: "btn btn-danger btn-lg btn-block" %> <% end %> ``` ### Internationalization (I18n) We use Rails locale files for error messages and scope descriptions, see [config/locales/en.yml](config/locales/en.yml). You can override these by adding them to your own translations in `config/locale`. ## Development Run `bundle install` to setup all development dependencies. To run all specs: ```sh bundle exec rake spec ``` To generate and run migrations in the test application: ```sh bundle exec rake migrate ``` To run the local engine server: ```sh bundle exec rake server ``` By default, the latest Rails version is used. To use a specific version run: ``` rails=4.2.0 bundle update ``` ## License Doorkeeper::OpenidConnect is released under the [MIT License](http://www.opensource.org/licenses/MIT). ## Sponsors Initial development of this project was sponsored by [PlayOn! Sports](https://github.com/playon). doorkeeper-openid_connect-1.8.7/Rakefile 0000664 0000000 0000000 00000001024 14431346656 0020331 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true ENV['RAILS_ENV'] ||= 'test' require 'bundler/gem_tasks' require 'rspec/core/rake_task' RSpec::Core::RakeTask.new task default: :spec task test: :spec desc 'Generate and run migrations in the test application' task :migrate do Dir.chdir('spec/dummy') do system('bin/rails generate doorkeeper:openid_connect:migration') system('bin/rake db:migrate') end end desc 'Run server in the test application' task :server do Dir.chdir('spec/dummy') do system('bin/rails server') end end doorkeeper-openid_connect-1.8.7/app/ 0000775 0000000 0000000 00000000000 14431346656 0017447 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/app/controllers/ 0000775 0000000 0000000 00000000000 14431346656 0022015 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/app/controllers/concerns/ 0000775 0000000 0000000 00000000000 14431346656 0023627 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/app/controllers/concerns/doorkeeper/ 0000775 0000000 0000000 00000000000 14431346656 0025766 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/app/controllers/concerns/doorkeeper/openid_connect/ 0000775 0000000 0000000 00000000000 14431346656 0030755 5 ustar 00root root 0000000 0000000 authorizations_extension.rb 0000664 0000000 0000000 00000000265 14431346656 0036405 0 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/app/controllers/concerns/doorkeeper/openid_connect module Doorkeeper module OpenidConnect module AuthorizationsExtension private def pre_auth_param_fields super.append(:nonce) end end end end doorkeeper-openid_connect-1.8.7/app/controllers/doorkeeper/ 0000775 0000000 0000000 00000000000 14431346656 0024154 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/app/controllers/doorkeeper/openid_connect/ 0000775 0000000 0000000 00000000000 14431346656 0027143 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/app/controllers/doorkeeper/openid_connect/discovery_controller.rb 0000664 0000000 0000000 00000010402 14431346656 0033737 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OpenidConnect class DiscoveryController < ::Doorkeeper::ApplicationMetalController include Doorkeeper::Helpers::Controller WEBFINGER_RELATION = 'http://openid.net/specs/connect/1.0/issuer' def provider render json: provider_response end def webfinger render json: webfinger_response end def keys render json: keys_response end private def provider_response doorkeeper = ::Doorkeeper.configuration openid_connect = ::Doorkeeper::OpenidConnect.configuration { issuer: issuer, authorization_endpoint: oauth_authorization_url(authorization_url_options), token_endpoint: oauth_token_url(token_url_options), revocation_endpoint: oauth_revoke_url(revocation_url_options), introspection_endpoint: respond_to?(:oauth_introspect_url) ? oauth_introspect_url(introspection_url_options) : nil, userinfo_endpoint: oauth_userinfo_url(userinfo_url_options), jwks_uri: oauth_discovery_keys_url(jwks_url_options), end_session_endpoint: instance_exec(&openid_connect.end_session_endpoint), scopes_supported: doorkeeper.scopes, # TODO: support id_token response type response_types_supported: doorkeeper.authorization_response_types, response_modes_supported: response_modes_supported(doorkeeper), grant_types_supported: grant_types_supported(doorkeeper), # TODO: look into doorkeeper-jwt_assertion for these # 'client_secret_jwt', # 'private_key_jwt' token_endpoint_auth_methods_supported: %w[client_secret_basic client_secret_post], subject_types_supported: openid_connect.subject_types_supported, id_token_signing_alg_values_supported: [ ::Doorkeeper::OpenidConnect.signing_algorithm ], claim_types_supported: [ 'normal', # TODO: support these # 'aggregated', # 'distributed', ], claims_supported: %w[ iss sub aud exp iat ] | openid_connect.claims.to_h.keys, code_challenge_methods_supported: code_challenge_methods_supported(doorkeeper), }.compact end def grant_types_supported(doorkeeper) grant_types_supported = doorkeeper.grant_flows.dup grant_types_supported << 'refresh_token' if doorkeeper.refresh_token_enabled? grant_types_supported end def response_modes_supported(doorkeeper) doorkeeper.authorization_response_flows.flat_map(&:response_mode_matches).uniq end def code_challenge_methods_supported(doorkeeper) return unless doorkeeper.access_grant_model.pkce_supported? %w[plain S256] end def webfinger_response { subject: params.require(:resource), links: [ { rel: WEBFINGER_RELATION, href: root_url(webfinger_url_options), } ] } end def keys_response signing_key = Doorkeeper::OpenidConnect.signing_key_normalized { keys: [ signing_key.merge( use: 'sig', alg: Doorkeeper::OpenidConnect.signing_algorithm ) ] } end def protocol Doorkeeper::OpenidConnect.configuration.protocol.call end def discovery_url_options Doorkeeper::OpenidConnect.configuration.discovery_url_options.call(request) end def discovery_url_default_options { protocol: protocol } end def issuer if Doorkeeper::OpenidConnect.configuration.issuer.respond_to?(:call) Doorkeeper::OpenidConnect.configuration.issuer.call(request).to_s else Doorkeeper::OpenidConnect.configuration.issuer end end %i[authorization token revocation introspection userinfo jwks webfinger].each do |endpoint| define_method :"#{endpoint}_url_options" do discovery_url_default_options.merge(discovery_url_options[endpoint.to_sym] || {}) end end end end end doorkeeper-openid_connect-1.8.7/app/controllers/doorkeeper/openid_connect/userinfo_controller.rb 0000664 0000000 0000000 00000000521 14431346656 0033563 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OpenidConnect class UserinfoController < ::Doorkeeper::ApplicationMetalController before_action -> { doorkeeper_authorize! :openid } def show render json: Doorkeeper::OpenidConnect::UserInfo.new(doorkeeper_token), status: :ok end end end end doorkeeper-openid_connect-1.8.7/bin/ 0000775 0000000 0000000 00000000000 14431346656 0017437 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/bin/console 0000775 0000000 0000000 00000000242 14431346656 0021025 0 ustar 00root root 0000000 0000000 #!/usr/bin/env ruby # frozen_string_literal: true require 'bundler/setup' Bundler.require :default require 'doorkeeper/openid_connect' require 'pry' Pry.start doorkeeper-openid_connect-1.8.7/bin/setup 0000775 0000000 0000000 00000000203 14431346656 0020520 0 ustar 00root root 0000000 0000000 #!/usr/bin/env bash set -euo pipefail IFS=$'\n\t' set -vx bundle install # Do any other automated setup that you need to do here doorkeeper-openid_connect-1.8.7/config/ 0000775 0000000 0000000 00000000000 14431346656 0020134 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/config/locales/ 0000775 0000000 0000000 00000000000 14431346656 0021556 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/config/locales/en.yml 0000664 0000000 0000000 00000002745 14431346656 0022713 0 ustar 00root root 0000000 0000000 en: doorkeeper: scopes: openid: 'Authenticate your account' profile: 'View your profile information' email: 'View your email address' address: 'View your physical address' phone: 'View your phone number' errors: messages: login_required: 'The authorization server requires end-user authentication' consent_required: 'The authorization server requires end-user consent' interaction_required: 'The authorization server requires end-user interaction' account_selection_required: 'The authorization server requires end-user account selection' openid_connect: errors: messages: # Configuration error messages resource_owner_from_access_token_not_configured: 'Failure due to Doorkeeper::OpenidConnect.configure.resource_owner_from_access_token missing configuration.' auth_time_from_resource_owner_not_configured: 'Failure due to Doorkeeper::OpenidConnect.configure.auth_time_from_resource_owner missing configuration.' reauthenticate_resource_owner_not_configured: 'Failure due to Doorkeeper::OpenidConnect.configure.reauthenticate_resource_owner missing configuration.' select_account_for_resource_owner_not_configured: 'Failure due to Doorkeeper::OpenidConnect.configure.select_account_for_resource_owner missing configuration.' subject_not_configured: 'ID Token generation failed due to Doorkeeper::OpenidConnect.configure.subject missing configuration.' doorkeeper-openid_connect-1.8.7/doorkeeper-openid_connect.gemspec 0000664 0000000 0000000 00000002461 14431346656 0025363 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true $LOAD_PATH.push File.expand_path('lib', __dir__) require 'doorkeeper/openid_connect/version' Gem::Specification.new do |spec| spec.name = 'doorkeeper-openid_connect' spec.version = Doorkeeper::OpenidConnect::VERSION spec.authors = ['Sam Dengler', 'Markus Koller', 'Nikita Bulai'] spec.email = ['sam.dengler@playonsports.com', 'markus-koller@gmx.ch', 'bulajnikita@gmail.com'] spec.homepage = 'https://github.com/doorkeeper-gem/doorkeeper-openid_connect' spec.summary = 'OpenID Connect extension for Doorkeeper.' spec.description = 'OpenID Connect extension for Doorkeeper.' spec.license = 'MIT' spec.files = Dir[ "{app,config,lib}/**/*", "CHANGELOG.md", "LICENSE.txt", "README.md", ] spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ['lib'] spec.required_ruby_version = '>= 2.7' spec.add_runtime_dependency 'doorkeeper', '>= 5.5', '< 5.7' spec.add_runtime_dependency 'jwt', '>= 2.5' spec.add_development_dependency 'conventional-changelog', '~> 1.2' spec.add_development_dependency 'factory_bot' spec.add_development_dependency 'pry-byebug' spec.add_development_dependency 'rspec-rails' spec.add_development_dependency 'sqlite3', '>= 1.3.6' end doorkeeper-openid_connect-1.8.7/gemfiles/ 0000775 0000000 0000000 00000000000 14431346656 0020462 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/gemfiles/doorkeeper_master.gemfile 0000664 0000000 0000000 00000000426 14431346656 0025530 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true source 'https://rubygems.org' gem 'rails', '~> 7.0.0' gem 'rails-controller-testing' gem 'sqlite3', '~> 1.4', platform: %i[ruby mswin mingw x64_mingw] gem 'doorkeeper', git: 'https://github.com/doorkeeper-gem/doorkeeper.git' gemspec path: '../' doorkeeper-openid_connect-1.8.7/gemfiles/rails_6.0.gemfile 0000664 0000000 0000000 00000000314 14431346656 0023507 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true source 'https://rubygems.org' gem 'rails', '~> 6.0.0' gem 'rails-controller-testing' gem 'sqlite3', '~> 1.4', platform: %i[ruby mswin mingw x64_mingw] gemspec path: '../' doorkeeper-openid_connect-1.8.7/gemfiles/rails_6.1.gemfile 0000664 0000000 0000000 00000000314 14431346656 0023510 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true source 'https://rubygems.org' gem 'rails', '~> 6.1.0' gem 'rails-controller-testing' gem 'sqlite3', '~> 1.4', platform: %i[ruby mswin mingw x64_mingw] gemspec path: '../' doorkeeper-openid_connect-1.8.7/gemfiles/rails_7.0.gemfile 0000664 0000000 0000000 00000000314 14431346656 0023510 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true source 'https://rubygems.org' gem 'rails', '~> 7.0.0' gem 'rails-controller-testing' gem 'sqlite3', '~> 1.4', platform: %i[ruby mswin mingw x64_mingw] gemspec path: '../' doorkeeper-openid_connect-1.8.7/lib/ 0000775 0000000 0000000 00000000000 14431346656 0017435 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/lib/doorkeeper/ 0000775 0000000 0000000 00000000000 14431346656 0021574 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/lib/doorkeeper/oauth/ 0000775 0000000 0000000 00000000000 14431346656 0022714 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/lib/doorkeeper/oauth/id_token_request.rb 0000664 0000000 0000000 00000001464 14431346656 0026612 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OAuth class IdTokenRequest attr_accessor :pre_auth, :auth, :resource_owner def initialize(pre_auth, resource_owner) @pre_auth = pre_auth @resource_owner = resource_owner end def authorize @auth = Authorization::Token.new(pre_auth, resource_owner) if @auth.respond_to?(:issue_token!) @auth.issue_token! else @auth.issue_token end response end def deny pre_auth.error = :access_denied pre_auth.error_response end private def response id_token = Doorkeeper::OpenidConnect::IdToken.new(auth.token, pre_auth.nonce) IdTokenResponse.new(pre_auth, auth, id_token) end end end end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/oauth/id_token_response.rb 0000664 0000000 0000000 00000001243 14431346656 0026753 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OAuth class IdTokenResponse < BaseResponse include OAuth::Helpers attr_accessor :pre_auth, :auth, :id_token def initialize(pre_auth, auth, id_token) @pre_auth = pre_auth @auth = auth @id_token = id_token end def redirectable? true end def body { expires_in: auth.token.expires_in_seconds, state: pre_auth.state, id_token: id_token.as_jws_token } end def redirect_uri Authorization::URIBuilder.uri_with_fragment(pre_auth.redirect_uri, body) end end end end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/oauth/id_token_token_request.rb 0000664 0000000 0000000 00000000520 14431346656 0030002 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OAuth class IdTokenTokenRequest < IdTokenRequest private def response id_token_token = Doorkeeper::OpenidConnect::IdTokenToken.new(auth.token, pre_auth.nonce) IdTokenTokenResponse.new(pre_auth, auth, id_token_token) end end end end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/oauth/id_token_token_response.rb 0000664 0000000 0000000 00000000423 14431346656 0030152 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OAuth class IdTokenTokenResponse < IdTokenResponse def body super.merge({ access_token: auth.token.token, token_type: auth.token.token_type }) end end end end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect.rb 0000664 0000000 0000000 00000004564 14431346656 0025121 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true require 'doorkeeper' require 'active_model' require 'jwt' require 'doorkeeper/request' require 'doorkeeper/request/id_token' require 'doorkeeper/request/id_token_token' require 'doorkeeper/oauth/id_token_request' require 'doorkeeper/oauth/id_token_token_request' require 'doorkeeper/oauth/id_token_response' require 'doorkeeper/oauth/id_token_token_response' require 'doorkeeper/openid_connect/claims_builder' require 'doorkeeper/openid_connect/claims/claim' require 'doorkeeper/openid_connect/claims/normal_claim' require 'doorkeeper/openid_connect/config' require 'doorkeeper/openid_connect/engine' require 'doorkeeper/openid_connect/errors' require 'doorkeeper/openid_connect/id_token' require 'doorkeeper/openid_connect/id_token_token' require 'doorkeeper/openid_connect/user_info' require 'doorkeeper/openid_connect/version' require 'doorkeeper/openid_connect/helpers/controller' require 'doorkeeper/openid_connect/oauth/authorization/code' require 'doorkeeper/openid_connect/oauth/authorization_code_request' require 'doorkeeper/openid_connect/oauth/password_access_token_request' require 'doorkeeper/openid_connect/oauth/pre_authorization' require 'doorkeeper/openid_connect/oauth/token_response' require 'doorkeeper/openid_connect/orm/active_record' require 'doorkeeper/openid_connect/rails/routes' module Doorkeeper module OpenidConnect def self.signing_algorithm configuration.signing_algorithm.to_s.upcase.to_sym end def self.signing_key key = if %i[HS256 HS384 HS512].include?(signing_algorithm) configuration.signing_key else OpenSSL::PKey.read(configuration.signing_key) end ::JWT::JWK.new(key, { kid_generator: ::JWT::JWK::Thumbprint }) end def self.signing_key_normalized signing_key.export end Doorkeeper::GrantFlow.register( :id_token, response_type_matches: 'id_token', response_mode_matches: %w[fragment form_post], response_type_strategy: Doorkeeper::Request::IdToken, ) Doorkeeper::GrantFlow.register( 'id_token token', response_type_matches: 'id_token token', response_mode_matches: %w[fragment form_post], response_type_strategy: Doorkeeper::Request::IdTokenToken, ) Doorkeeper::GrantFlow.register_alias( 'implicit_oidc', as: ['implicit', 'id_token', 'id_token token'] ) end end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/ 0000775 0000000 0000000 00000000000 14431346656 0024563 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/claims/ 0000775 0000000 0000000 00000000000 14431346656 0026033 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/claims/aggregated_claim.rb 0000664 0000000 0000000 00000000265 14431346656 0031622 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OpenidConnect module Claims class AggregatedClaim < Claim attr_accessor :jwt end end end end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/claims/claim.rb 0000664 0000000 0000000 00000002220 14431346656 0027441 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OpenidConnect module Claims class Claim attr_accessor :name, :response, :scope # http://openid.net/specs/openid-connect-core-1_0.html#StandardClaims # http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims STANDARD_CLAIMS = { profile: %i[ name family_name given_name middle_name nickname preferred_username profile picture website gender birthdate zoneinfo locale updated_at ], email: %i[email email_verified], address: %i[address], phone: %i[phone_number phone_number_verified], }.freeze def initialize(options = {}) @name = options[:name].to_sym @response = Array.wrap(options[:response]) @scope = options[:scope].to_sym if options[:scope] # use default scope for Standard Claims @scope ||= STANDARD_CLAIMS.find do |_scope, claims| claims.include? @name end.try(:first) # use profile scope as default fallback @scope ||= :profile end end end end end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/claims/distributed_claim.rb 0000664 0000000 0000000 00000000312 14431346656 0032043 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OpenidConnect module Claims class DistributedClaim < Claim attr_accessor :endpoint, :access_token end end end end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/claims/normal_claim.rb 0000664 0000000 0000000 00000000533 14431346656 0031016 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OpenidConnect module Claims class NormalClaim < Claim attr_reader :generator def initialize(options = {}) super(options) @generator = options[:generator] end def type :normal end end end end end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/claims_builder.rb 0000664 0000000 0000000 00000002037 14431346656 0030070 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true require 'ostruct' module Doorkeeper module OpenidConnect class ClaimsBuilder def self.generate(access_token, response) resource_owner = Doorkeeper::OpenidConnect.configuration.resource_owner_from_access_token.call(access_token) Doorkeeper::OpenidConnect.configuration.claims.to_h.map do |name, claim| if access_token.scopes.exists?(claim.scope) && claim.response.include?(response) [name, claim.generator.call(resource_owner, access_token.scopes, access_token)] end end.compact.to_h end def initialize(&block) @claims = OpenStruct.new instance_eval(&block) end def build @claims end def normal_claim(name, response: [:user_info], scope: nil, &block) @claims[name] = Claims::NormalClaim.new( name: name, response: response, scope: scope, generator: block ) end alias claim normal_claim end end end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/config.rb 0000664 0000000 0000000 00000005514 14431346656 0026362 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OpenidConnect def self.configure(&block) if Doorkeeper.configuration.orm != :active_record raise Errors::InvalidConfiguration, 'Doorkeeper OpenID Connect currently only supports the ActiveRecord ORM adapter' end @config = Config::Builder.new(&block).build end def self.configuration @config || (raise Errors::MissingConfiguration) end class Config class Builder def initialize(&block) @config = Config.new instance_eval(&block) end def build @config end def jws_public_key(*_args) puts 'DEPRECATION WARNING: `jws_public_key` is not needed anymore and will be removed in a future version, please remove it from config/initializers/doorkeeper_openid_connect.rb' end def jws_private_key(*args) puts 'DEPRECATION WARNING: `jws_private_key` has been replaced by `signing_key` and will be removed in a future version, please remove it from config/initializers/doorkeeper_openid_connect.rb' signing_key(*args) end end mattr_reader(:builder_class) { Config::Builder } extend ::Doorkeeper::Config::Option option :issuer option :signing_key option :signing_algorithm, default: :rs256 option :subject_types_supported, default: [:public] option :resource_owner_from_access_token, default: lambda { |*_| raise Errors::InvalidConfiguration, I18n.translate('doorkeeper.openid_connect.errors.messages.resource_owner_from_access_token_not_configured') } option :auth_time_from_resource_owner, default: lambda { |*_| raise Errors::InvalidConfiguration, I18n.translate('doorkeeper.openid_connect.errors.messages.auth_time_from_resource_owner_not_configured') } option :reauthenticate_resource_owner, default: lambda { |*_| raise Errors::InvalidConfiguration, I18n.translate('doorkeeper.openid_connect.errors.messages.reauthenticate_resource_owner_not_configured') } option :select_account_for_resource_owner, default: lambda { |*_| raise Errors::InvalidConfiguration, I18n.translate('doorkeeper.openid_connect.errors.messages.select_account_for_resource_owner_not_configured') } option :subject, default: lambda { |*_| raise Errors::InvalidConfiguration, I18n.translate('doorkeeper.openid_connect.errors.messages.subject_not_configured') } option :expiration, default: 120 option :claims, builder_class: ClaimsBuilder option :protocol, default: lambda { |*_| ::Rails.env.production? ? :https : :http } option :end_session_endpoint, default: lambda { |*_| nil } option :discovery_url_options, default: lambda { |*_| {} } end end end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/engine.rb 0000664 0000000 0000000 00000000607 14431346656 0026360 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OpenidConnect class Engine < ::Rails::Engine initializer 'doorkeeper.openid_connect.routes' do Doorkeeper::OpenidConnect::Rails::Routes.install! end config.to_prepare do Doorkeeper::AuthorizationsController.prepend Doorkeeper::OpenidConnect::AuthorizationsExtension end end end end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/errors.rb 0000664 0000000 0000000 00000001704 14431346656 0026426 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OpenidConnect module Errors class OpenidConnectError < StandardError def type self.class.name.demodulize.underscore.to_sym end end # internal errors class InvalidConfiguration < OpenidConnectError; end class MissingConfiguration < OpenidConnectError def initialize super('Configuration for Doorkeeper OpenID Connect missing. Do you have doorkeeper_openid_connect initializer?') end end # OAuth 2.0 errors # https://tools.ietf.org/html/rfc6749#section-4.1.2.1 class InvalidRequest < OpenidConnectError; end # OpenID Connect 1.0 errors # http://openid.net/specs/openid-connect-core-1_0.html#AuthError class LoginRequired < OpenidConnectError; end class ConsentRequired < OpenidConnectError; end class InteractionRequired < OpenidConnectError; end end end end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/helpers/ 0000775 0000000 0000000 00000000000 14431346656 0026225 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/helpers/controller.rb 0000664 0000000 0000000 00000011734 14431346656 0030743 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OpenidConnect module Helpers module Controller private # FIXME: remove after Doorkeeper will merge it def current_resource_owner return @current_resource_owner if defined?(@current_resource_owner) super end def authenticate_resource_owner! super.tap do |owner| next unless oidc_authorization_request? handle_oidc_prompt_param!(owner) handle_oidc_max_age_param!(owner) end rescue Errors::OpenidConnectError => e handle_oidc_error!(e) end def oidc_authorization_request? controller_path == Doorkeeper::Rails::Routes.mapping[:authorizations][:controllers] && action_name == 'new' && pre_auth.valid? && pre_auth.scopes.include?('openid') end def handle_oidc_error!(exception) # clear the previous response body to avoid a DoubleRenderError self.response_body = nil # FIXME: workaround for Rails 5, see https://github.com/rails/rails/issues/25106 @_response_body = nil error_response = if exception.type == :invalid_request ::Doorkeeper::OAuth::InvalidRequestResponse.new( name: exception.type, state: params[:state], redirect_uri: params[:redirect_uri], response_on_fragment: pre_auth.response_on_fragment?, ) else ::Doorkeeper::OAuth::ErrorResponse.new( name: exception.type, state: params[:state], redirect_uri: params[:redirect_uri], response_on_fragment: pre_auth.response_on_fragment?, ) end response.headers.merge!(error_response.headers) # NOTE: Assign error_response to @authorize_response then use redirect_or_render method that are defined at # doorkeeper's authorizations_controller. # - https://github.com/doorkeeper-gem/doorkeeper/blob/v5.5.0/app/controllers/doorkeeper/authorizations_controller.rb#L110 # - https://github.com/doorkeeper-gem/doorkeeper/blob/v5.5.0/app/controllers/doorkeeper/authorizations_controller.rb#L52 @authorize_response = error_response redirect_or_render(@authorize_response) end def handle_oidc_prompt_param!(owner) prompt_values ||= params[:prompt].to_s.split(/ +/).uniq prompt_values.each do |prompt| case prompt when 'none' raise Errors::InvalidRequest if (prompt_values - ['none']).any? raise Errors::LoginRequired unless owner raise Errors::ConsentRequired if oidc_consent_required? when 'login' reauthenticate_oidc_resource_owner(owner) if owner when 'consent' render :new if owner when 'select_account' select_account_for_oidc_resource_owner(owner) else raise Errors::InvalidRequest end end end def handle_oidc_max_age_param!(owner) max_age = params[:max_age].to_i return unless max_age > 0 && owner auth_time = instance_exec( owner, &Doorkeeper::OpenidConnect.configuration.auth_time_from_resource_owner ) if !auth_time || (Time.zone.now - auth_time) > max_age reauthenticate_oidc_resource_owner(owner) end end def return_without_oidc_prompt_param(prompt_value) return_to = URI.parse(request.path) return_to.query = request.query_parameters.tap do |params| params['prompt'] = params['prompt'].to_s.sub(/\b#{prompt_value}\s*\b/, '').strip params.delete('prompt') if params['prompt'].blank? end.to_query return_to.to_s end def reauthenticate_oidc_resource_owner(owner) return_to = return_without_oidc_prompt_param('login') instance_exec( owner, return_to, &Doorkeeper::OpenidConnect.configuration.reauthenticate_resource_owner ) raise Errors::LoginRequired unless performed? end def oidc_consent_required? !skip_authorization? && !matching_token? end def select_account_for_oidc_resource_owner(owner) return_to = return_without_oidc_prompt_param('select_account') instance_exec( owner, return_to, &Doorkeeper::OpenidConnect.configuration.select_account_for_resource_owner ) end end end end Helpers::Controller.prepend OpenidConnect::Helpers::Controller end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/id_token.rb 0000664 0000000 0000000 00000003603 14431346656 0026706 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OpenidConnect class IdToken include ActiveModel::Validations attr_reader :nonce def initialize(access_token, nonce = nil) @access_token = access_token @nonce = nonce @resource_owner = Doorkeeper::OpenidConnect.configuration.resource_owner_from_access_token.call(access_token) @issued_at = Time.zone.now end def claims { iss: issuer, sub: subject, aud: audience, exp: expiration, iat: issued_at, nonce: nonce, auth_time: auth_time }.merge ClaimsBuilder.generate(@access_token, :id_token) end def as_json(*_) claims.reject { |_, value| value.nil? || value == '' } end def as_jws_token ::JWT.encode(as_json, Doorkeeper::OpenidConnect.signing_key.keypair, Doorkeeper::OpenidConnect.signing_algorithm.to_s, { kid: Doorkeeper::OpenidConnect.signing_key.kid } ).to_s end private def issuer if Doorkeeper::OpenidConnect.configuration.issuer.respond_to?(:call) Doorkeeper::OpenidConnect.configuration.issuer.call(@resource_owner, @access_token.application).to_s else Doorkeeper::OpenidConnect.configuration.issuer end end def subject Doorkeeper::OpenidConnect.configuration.subject.call(@resource_owner, @access_token.application).to_s end def audience @access_token.application.try(:uid) end def expiration (@issued_at.utc + Doorkeeper::OpenidConnect.configuration.expiration).to_i end def issued_at @issued_at.utc.to_i end def auth_time Doorkeeper::OpenidConnect.configuration.auth_time_from_resource_owner.call(@resource_owner).try(:to_i) end end end end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/id_token_token.rb 0000664 0000000 0000000 00000002475 14431346656 0030114 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OpenidConnect class IdTokenToken < IdToken def claims super.merge(at_hash: at_hash) end private # The at_hash is build according to the following standard: # # http://openid.net/specs/openid-connect-implicit-1_0.html#IDToken # # at_hash: # REQUIRED. Access Token hash value. If the ID Token is issued with an # access_token in an Implicit Flow, this is REQUIRED, which is the case # for this subset of OpenID Connect. Its value is the base64url encoding # of the left-most half of the hash of the octets of the ASCII # representation of the access_token value, where the hash algorithm # used is the hash algorithm used in the alg Header Parameter of the # ID Token's JOSE Header. For instance, if the alg is RS256, hash the # access_token value with SHA-256, then take the left-most 128 bits and # base64url-encode them. The at_hash value is a case-sensitive string. def at_hash sha256 = Digest::SHA256.new token = @access_token.token hashed_token = sha256.digest(token) first_half = hashed_token[0...hashed_token.length / 2] Base64.urlsafe_encode64(first_half).tr('=', '') end end end end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/oauth/ 0000775 0000000 0000000 00000000000 14431346656 0025703 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/oauth/authorization/ 0000775 0000000 0000000 00000000000 14431346656 0030603 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/oauth/authorization/code.rb 0000664 0000000 0000000 00000002077 14431346656 0032050 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OpenidConnect module OAuth module Authorization module Code if Doorkeeper::OAuth::Authorization::Code.method_defined?(:issue_token!) def issue_token! super.tap do |access_grant| create_openid_request(access_grant) if pre_auth.nonce.present? end end alias issue_token issue_token! else # FIXME: drop this after dropping support of Doorkeeper < 5.4 def issue_token super.tap do |access_grant| create_openid_request(access_grant) if pre_auth.nonce.present? end end end private def create_openid_request(access_grant) ::Doorkeeper::OpenidConnect::Request.create!( access_grant: access_grant, nonce: pre_auth.nonce ) end end end end end OAuth::Authorization::Code.prepend OpenidConnect::OAuth::Authorization::Code end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/oauth/authorization_code_request.rb 0000664 0000000 0000000 00000001145 14431346656 0033673 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OpenidConnect module OAuth module AuthorizationCodeRequest private def after_successful_response super nonce = if (openid_request = grant.openid_request) openid_request.destroy! openid_request.nonce end id_token = Doorkeeper::OpenidConnect::IdToken.new(access_token, nonce) @response.id_token = id_token end end end end OAuth::AuthorizationCodeRequest.prepend OpenidConnect::OAuth::AuthorizationCodeRequest end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/oauth/password_access_token_request.rb0000664 0000000 0000000 00000001600 14431346656 0034360 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OpenidConnect module OAuth module PasswordAccessTokenRequest attr_reader :nonce if Gem.loaded_specs['doorkeeper'].version >= Gem::Version.create('5.5.1') def initialize(server, client, credentials, resource_owner, parameters = {}) super @nonce = parameters[:nonce] end else def initialize(server, client, resource_owner, parameters = {}) super @nonce = parameters[:nonce] end end private def after_successful_response id_token = Doorkeeper::OpenidConnect::IdToken.new(access_token, nonce) @response.id_token = id_token super end end end end OAuth::PasswordAccessTokenRequest.prepend OpenidConnect::OAuth::PasswordAccessTokenRequest end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/oauth/pre_authorization.rb 0000664 0000000 0000000 00000001577 14431346656 0032010 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OpenidConnect module OAuth module PreAuthorization attr_reader :nonce def initialize(server, attrs = {}, resource_owner = nil) super @nonce = attrs[:nonce] end # NOTE: Auto get default response_mode of specified response_type if response_mode is not # yet present. We can delete this method after Doorkeeper's minimize version support it. def response_on_fragment? return response_mode == 'fragment' if response_mode.present? grant_flow = server.authorization_response_flows.detect do |flow| flow.matches_response_type?(response_type) end grant_flow&.default_response_mode == 'fragment' end end end end OAuth::PreAuthorization.prepend OpenidConnect::OAuth::PreAuthorization end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/oauth/token_response.rb 0000664 0000000 0000000 00000001076 14431346656 0031272 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OpenidConnect module OAuth module TokenResponse attr_accessor :id_token def body if token.includes_scope? 'openid' id_token = self.id_token || Doorkeeper::OpenidConnect::IdToken.new(token) super .merge(id_token: id_token.as_jws_token) .reject { |_, value| value.blank? } else super end end end end end OAuth::TokenResponse.prepend OpenidConnect::OAuth::TokenResponse end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/orm/ 0000775 0000000 0000000 00000000000 14431346656 0025360 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/orm/active_record.rb 0000664 0000000 0000000 00000003730 14431346656 0030521 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true require 'active_support/lazy_load_hooks' module Doorkeeper module OpenidConnect autoload :AccessGrant, "doorkeeper/openid_connect/orm/active_record/access_grant" autoload :Request, "doorkeeper/openid_connect/orm/active_record/request" module Orm module ActiveRecord def run_hooks super if Gem.loaded_specs['doorkeeper'].version >= Gem::Version.create('5.5.0') Doorkeeper.config.access_grant_model.prepend Doorkeeper::OpenidConnect::AccessGrant else Doorkeeper::AccessGrant.prepend Doorkeeper::OpenidConnect::AccessGrant end if Doorkeeper.configuration.respond_to?(:active_record_options) && Doorkeeper.configuration.active_record_options[:establish_connection] [Doorkeeper::OpenidConnect::Request].each do |c| c.send :establish_connection, Doorkeeper.configuration.active_record_options[:establish_connection] end end end def initialize_models! super ActiveSupport.on_load(:active_record) do require 'doorkeeper/openid_connect/orm/active_record/access_grant' require 'doorkeeper/openid_connect/orm/active_record/request' if Gem.loaded_specs['doorkeeper'].version >= Gem::Version.create('5.5.0') Doorkeeper.config.access_grant_model.prepend Doorkeeper::OpenidConnect::AccessGrant else Doorkeeper::AccessGrant.prepend Doorkeeper::OpenidConnect::AccessGrant end if Doorkeeper.configuration.active_record_options[:establish_connection] [Doorkeeper::OpenidConnect::Request].each do |c| c.send :establish_connection, Doorkeeper.configuration.active_record_options[:establish_connection] end end end end end end end Orm::ActiveRecord.singleton_class.send :prepend, OpenidConnect::Orm::ActiveRecord end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/orm/active_record/ 0000775 0000000 0000000 00000000000 14431346656 0030171 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/orm/active_record/access_grant.rb 0000664 0000000 0000000 00000000624 14431346656 0033154 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OpenidConnect module AccessGrant def self.prepended(base) base.class_eval do has_one :openid_request, class_name: 'Doorkeeper::OpenidConnect::Request', foreign_key: 'access_grant_id', inverse_of: :access_grant, dependent: :delete end end end end end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/orm/active_record/request.rb 0000664 0000000 0000000 00000001250 14431346656 0032204 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OpenidConnect class Request < ::ActiveRecord::Base self.table_name = "#{table_name_prefix}oauth_openid_requests#{table_name_suffix}".to_sym validates :access_grant_id, :nonce, presence: true if Gem.loaded_specs['doorkeeper'].version >= Gem::Version.create('5.5.0') belongs_to :access_grant, class_name: Doorkeeper.config.access_grant_class.to_s, inverse_of: :openid_request else belongs_to :access_grant, class_name: 'Doorkeeper::AccessGrant', inverse_of: :openid_request end end end end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/rails/ 0000775 0000000 0000000 00000000000 14431346656 0025675 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/rails/routes.rb 0000664 0000000 0000000 00000003637 14431346656 0027554 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true require 'doorkeeper/openid_connect/rails/routes/mapping' require 'doorkeeper/openid_connect/rails/routes/mapper' module Doorkeeper module OpenidConnect module Rails class Routes module Helper def use_doorkeeper_openid_connect(options = {}, &block) Doorkeeper::OpenidConnect::Rails::Routes.new(self, &block).generate_routes!(options) end end def self.install! ActionDispatch::Routing::Mapper.include Doorkeeper::OpenidConnect::Rails::Routes::Helper end attr_accessor :routes def initialize(routes, &block) @routes = routes @block = block end def generate_routes!(options) @mapping = Mapper.new.map(&@block) routes.scope options[:scope] || 'oauth', as: 'oauth' do map_route(:userinfo, :userinfo_routes) map_route(:discovery, :discovery_routes) end routes.scope as: 'oauth' do map_route(:discovery, :discovery_well_known_routes) end end private def map_route(name, method) return if @mapping.skipped?(name) mapping = @mapping[name] routes.scope controller: mapping[:controllers], as: mapping[:as] do send method end end def userinfo_routes routes.get :show, path: 'userinfo', as: '' routes.post :show, path: 'userinfo', as: nil end def discovery_routes routes.scope path: 'discovery' do routes.get :keys end end def discovery_well_known_routes routes.scope path: '.well-known' do routes.get :provider, path: 'openid-configuration' routes.get :provider, path: 'oauth-authorization-server' routes.get :webfinger end end end end end end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/rails/routes/ 0000775 0000000 0000000 00000000000 14431346656 0027216 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/rails/routes/mapper.rb 0000664 0000000 0000000 00000001266 14431346656 0031034 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OpenidConnect module Rails class Routes 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 end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/rails/routes/mapping.rb 0000664 0000000 0000000 00000001434 14431346656 0031200 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OpenidConnect module Rails class Routes class Mapping attr_accessor :controllers, :as, :skips def initialize @controllers = { userinfo: 'doorkeeper/openid_connect/userinfo', discovery: 'doorkeeper/openid_connect/discovery' } @as = { userinfo: :userinfo, discovery: :discovery } @skips = [] end def [](routes) { controllers: @controllers[routes], as: @as[routes] } end def skipped?(controller) @skips.include?(controller) end end end end end end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/user_info.rb 0000664 0000000 0000000 00000001502 14431346656 0027077 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OpenidConnect class UserInfo include ActiveModel::Validations def initialize(access_token) @access_token = access_token end def claims { sub: subject }.merge ClaimsBuilder.generate(@access_token, :user_info) end def as_json(*_) claims.reject { |_, value| value.nil? || value == '' } end private def subject Doorkeeper::OpenidConnect.configuration.subject.call(resource_owner, application).to_s end def resource_owner @resource_owner ||= Doorkeeper::OpenidConnect.configuration.resource_owner_from_access_token.call(@access_token) end def application @application ||= @access_token.application end end end end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/version.rb 0000664 0000000 0000000 00000000150 14431346656 0026571 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OpenidConnect VERSION = '1.8.7' end end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/request/ 0000775 0000000 0000000 00000000000 14431346656 0023264 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/lib/doorkeeper/request/id_token.rb 0000664 0000000 0000000 00000000577 14431346656 0025416 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true require 'doorkeeper/request/strategy' module Doorkeeper module Request class IdToken < Strategy delegate :current_resource_owner, to: :server def pre_auth server.context.send(:pre_auth) end def request @request ||= OAuth::IdTokenRequest.new(pre_auth, current_resource_owner) end end end end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/request/id_token_token.rb 0000664 0000000 0000000 00000000611 14431346656 0026603 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true require 'doorkeeper/request/strategy' module Doorkeeper module Request class IdTokenToken < Strategy delegate :current_resource_owner, to: :server def pre_auth server.context.send(:pre_auth) end def request @request ||= OAuth::IdTokenTokenRequest.new(pre_auth, current_resource_owner) end end end end doorkeeper-openid_connect-1.8.7/lib/generators/ 0000775 0000000 0000000 00000000000 14431346656 0021606 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/lib/generators/doorkeeper/ 0000775 0000000 0000000 00000000000 14431346656 0023745 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/lib/generators/doorkeeper/openid_connect/ 0000775 0000000 0000000 00000000000 14431346656 0026734 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/lib/generators/doorkeeper/openid_connect/install_generator.rb 0000664 0000000 0000000 00000001115 14431346656 0032773 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module Doorkeeper module OpenidConnect class InstallGenerator < ::Rails::Generators::Base include ::Rails::Generators::Migration source_root File.expand_path('templates', __dir__) desc 'Installs Doorkeeper OpenID Connect.' def install template 'initializer.rb', 'config/initializers/doorkeeper_openid_connect.rb' copy_file File.expand_path('../../../../config/locales/en.yml', __dir__), 'config/locales/doorkeeper_openid_connect.en.yml' route 'use_doorkeeper_openid_connect' end end end end doorkeeper-openid_connect-1.8.7/lib/generators/doorkeeper/openid_connect/migration_generator.rb 0000664 0000000 0000000 00000001562 14431346656 0033324 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true require 'rails/generators/active_record' module Doorkeeper module OpenidConnect class MigrationGenerator < ::Rails::Generators::Base include ::Rails::Generators::Migration source_root File.expand_path('templates', __dir__) desc 'Installs Doorkeeper OpenID Connect migration file.' def install migration_template( 'migration.rb.erb', 'db/migrate/create_doorkeeper_openid_connect_tables.rb', migration_version: migration_version ) end def self.next_migration_number(dirname) ActiveRecord::Generators::Base.next_migration_number(dirname) end private def migration_version if ActiveRecord::VERSION::MAJOR >= 5 "[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]" end end end end end doorkeeper-openid_connect-1.8.7/lib/generators/doorkeeper/openid_connect/templates/ 0000775 0000000 0000000 00000000000 14431346656 0030732 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/lib/generators/doorkeeper/openid_connect/templates/initializer.rb 0000664 0000000 0000000 00000004144 14431346656 0033605 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true Doorkeeper::OpenidConnect.configure do issuer do |resource_owner, application| 'issuer string' end signing_key <<~KEY -----BEGIN RSA PRIVATE KEY----- .... -----END RSA PRIVATE KEY----- KEY subject_types_supported [:public] resource_owner_from_access_token do |access_token| # Example implementation: # User.find_by(id: access_token.resource_owner_id) end auth_time_from_resource_owner do |resource_owner| # Example implementation: # resource_owner.current_sign_in_at end reauthenticate_resource_owner do |resource_owner, return_to| # Example implementation: # store_location_for resource_owner, return_to # sign_out resource_owner # redirect_to new_user_session_url end # Depending on your configuration, a DoubleRenderError could be raised # if render/redirect_to is called at some point before this callback is executed. # To avoid the DoubleRenderError, you could add these two lines at the beginning # of this callback: (Reference: https://github.com/rails/rails/issues/25106) # self.response_body = nil # @_response_body = nil select_account_for_resource_owner do |resource_owner, return_to| # Example implementation: # store_location_for resource_owner, return_to # redirect_to account_select_url end subject do |resource_owner, application| # Example implementation: # resource_owner.id # or if you need pairwise subject identifier, implement like below: # Digest::SHA256.hexdigest("#{resource_owner.id}#{URI.parse(application.redirect_uri).host}#{'your_secret_salt'}") end # Protocol to use when generating URIs for the discovery endpoint, # for example if you also use HTTPS in development # protocol do # :https # end # Expiration time on or after which the ID Token MUST NOT be accepted for processing. (default 120 seconds). # expiration 600 # Example claims: # claims do # normal_claim :_foo_ do |resource_owner| # resource_owner.foo # end # normal_claim :_bar_ do |resource_owner| # resource_owner.bar # end # end end doorkeeper-openid_connect-1.8.7/lib/generators/doorkeeper/openid_connect/templates/migration.rb.erb 0000664 0000000 0000000 00000000630 14431346656 0034016 0 ustar 00root root 0000000 0000000 class CreateDoorkeeperOpenidConnectTables < ActiveRecord::Migration<%= migration_version %> def change create_table :oauth_openid_requests do |t| t.references :access_grant, null: false, index: true t.string :nonce, null: false end add_foreign_key( :oauth_openid_requests, :oauth_access_grants, column: :access_grant_id, on_delete: :cascade ) end end doorkeeper-openid_connect-1.8.7/spec/ 0000775 0000000 0000000 00000000000 14431346656 0017621 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/controllers/ 0000775 0000000 0000000 00000000000 14431346656 0022167 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/controllers/discovery_controller_spec.rb 0000664 0000000 0000000 00000024025 14431346656 0030003 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true require 'rails_helper' describe Doorkeeper::OpenidConnect::DiscoveryController, type: :controller do describe '#provider' do it 'returns the provider configuration' do get :provider data = JSON.parse(response.body) expect(data.sort).to match({ 'issuer' => 'dummy', 'authorization_endpoint' => 'http://test.host/oauth/authorize', 'token_endpoint' => 'http://test.host/oauth/token', 'revocation_endpoint' => 'http://test.host/oauth/revoke', 'introspection_endpoint' => 'http://test.host/oauth/introspect', 'userinfo_endpoint' => 'http://test.host/oauth/userinfo', 'jwks_uri' => 'http://test.host/oauth/discovery/keys', 'scopes_supported' => ['openid'], 'response_types_supported' => ['code', 'token', 'id_token', 'id_token token'], 'response_modes_supported' => %w[query fragment form_post], 'grant_types_supported' => %w[authorization_code client_credentials implicit_oidc], 'token_endpoint_auth_methods_supported' => %w[client_secret_basic client_secret_post], 'subject_types_supported' => [ 'public', ], 'id_token_signing_alg_values_supported' => [ 'RS256', ], 'claim_types_supported' => [ 'normal', ], 'claims_supported' => %w[ iss sub aud exp iat name variable_name created_at updated_at token_id both_responses id_token_response user_info_response ], 'code_challenge_methods_supported' => %w[ plain S256 ], }.sort) end context 'when refresh_token grant type is enabled' do before { Doorkeeper.configure { use_refresh_token } } it 'add refresh_token to grant_types_supported' do get :provider data = JSON.parse(response.body) expect(data['grant_types_supported']).to eq %w[authorization_code client_credentials refresh_token] end end context 'when issuer block' do before { Doorkeeper::OpenidConnect.configure { issuer do |r, a| "test-issuer" end } } it 'return blocks result' do get :provider data = JSON.parse(response.body) expect(data['issuer']).to eq "test-issuer" end end context 'when grant_flows is configed with only client_credentials' do before { Doorkeeper.configure { grant_flows %w[client_credentials] } } it 'return empty response_modes_supported' do get :provider data = JSON.parse(response.body) expect(data['response_modes_supported']).to eq [] end end context 'when grant_flows is configed only implicit flow' do before { Doorkeeper.configure { grant_flows %w[implicit_oidc] } } it 'return fragment and form_post as response_modes_supported' do get :provider data = JSON.parse(response.body) expect(data['response_modes_supported']).to eq %w[fragment form_post] end end context 'when grant_flows is configed with authorization_code and implicit flow' do before { Doorkeeper.configure { grant_flows %w[authorization_code implicit_oidc] } } it 'return query, fragment and form_post as response_modes_supported' do get :provider data = JSON.parse(response.body) expect(data['response_modes_supported']).to eq %w[query fragment form_post] end end it 'uses the protocol option for generating URLs' do Doorkeeper::OpenidConnect.configure do protocol { :testing } end get :provider data = JSON.parse(response.body) expect(data['authorization_endpoint']).to eq 'testing://test.host/oauth/authorize' end context 'when the discovery_url_options option is set for all endpoints' do before do Doorkeeper::OpenidConnect.configure do discovery_url_options do |request| { authorization: { host: 'alternate-authorization.host' }, token: { host: 'alternate-token.host' }, revocation: { host: 'alternate-revocation.host' }, introspection: { host: 'alternate-introspection.host' }, userinfo: { host: 'alternate-userinfo.host' }, jwks: { host: 'alternate-jwks.host' } } end end end it 'uses the discovery_url_options option when generating the endpoint urls' do get :provider data = JSON.parse(response.body) expect(data['authorization_endpoint']).to eq 'http://alternate-authorization.host/oauth/authorize' expect(data['token_endpoint']).to eq 'http://alternate-token.host/oauth/token' expect(data['revocation_endpoint']).to eq 'http://alternate-revocation.host/oauth/revoke' expect(data['introspection_endpoint']).to eq 'http://alternate-introspection.host/oauth/introspect' expect(data['userinfo_endpoint']).to eq 'http://alternate-userinfo.host/oauth/userinfo' expect(data['jwks_uri']).to eq 'http://alternate-jwks.host/oauth/discovery/keys' end end context 'when the discovery_url_options option is only set for some endpoints' do before do Doorkeeper::OpenidConnect.configure do discovery_url_options do |request| { authorization: { host: 'alternate-authorization.host' } } end end end it 'does not use the discovery_url_options option when generating other URLs' do get :provider data = JSON.parse(response.body) { 'token_endpoint' => 'http://test.host/oauth/token', 'revocation_endpoint' => 'http://test.host/oauth/revoke', 'introspection_endpoint' => 'http://test.host/oauth/introspect', 'userinfo_endpoint' => 'http://test.host/oauth/userinfo', 'jwks_uri' => 'http://test.host/oauth/discovery/keys', }.each do |endpoint, expected_url| expect(data[endpoint]).to eq expected_url end end end it 'does not return an end session endpoint if none is configured' do get :provider data = JSON.parse(response.body) expect(data.key?('end_session_endpoint')).to be(false) end it 'uses the configured end session endpoint with self as context' do Doorkeeper::OpenidConnect.configure do end_session_endpoint -> { logout_url } end def controller.logout_url 'http://test.host/logout' end get :provider data = JSON.parse(response.body) expect(data['end_session_endpoint']).to eq 'http://test.host/logout' end context 'when token inspection is disallowed' do let(:doorkeeper_config) { Doorkeeper.config } let!(:allow_token_introspection) { doorkeeper_config.allow_token_introspection } before do allow(doorkeeper_config).to receive(:allow_token_introspection).and_return(false) Rails.application.reload_routes! end after do allow(doorkeeper_config).to receive(:allow_token_introspection).and_return(allow_token_introspection) Rails.application.reload_routes! end it 'does not return introspection_endpoint' do get :provider data = JSON.parse(response.body) expect(data.key?('introspection_endpoint')).to be(false) end end end describe '#webfinger' do it 'requires the resource parameter' do expect do get :webfinger end.to raise_error ActionController::ParameterMissing end it 'returns the OpenID Connect relation' do get :webfinger, params: { resource: 'user@example.com' } data = JSON.parse(response.body) expect(data.sort).to eq({ 'subject' => 'user@example.com', 'links' => [ 'rel' => 'http://openid.net/specs/connect/1.0/issuer', 'href' => 'http://test.host/', ], }.sort) end context 'when the discovery_url_options option is set for webfinger endpoint' do before do Doorkeeper::OpenidConnect.configure do discovery_url_options do |request| { webfinger: { host: 'alternate-webfinger.host' } } end end end it 'uses the discovery_url_options option when generating the webfinger endpoint url' do get :webfinger, params: { resource: 'user@example.com' } data = JSON.parse(response.body) expect(data['links'].first['href']).to eq 'http://alternate-webfinger.host/' end end context 'when the discovery_url_options option uses the request for an endpoint' do before do Doorkeeper::OpenidConnect.configure do discovery_url_options do |request| { authorization: { host: 'alternate-authorization.host', protocol: request.ssl? ? :https : :testing } } end end end it 'uses the discovery_url_options option when generating the webfinger endpoint url' do get :provider data = JSON.parse(response.body) expect(data['authorization_endpoint']).to eq 'testing://alternate-authorization.host/oauth/authorize' end end end describe '#keys' do subject { get :keys } shared_examples 'a key response' do |options| expected_parameters = options[:expected_parameters] it "includes only #{expected_parameters.join(', ')} parameters" do subject data = JSON.parse(response.body) key = data['keys'].first expect(key.keys.map(&:to_sym)).to match_array(expected_parameters) end end context 'when using an RSA key' do it_behaves_like 'a key response', expected_parameters: %i[kty kid e n use alg] end context 'when using an EC key' do before { configure_ec } it_behaves_like 'a key response', expected_parameters: %i[kty kid crv x y use alg] end context 'when using an HMAC key' do before { configure_hmac } it_behaves_like 'a key response', expected_parameters: %i[kty kid use alg] end end end doorkeeper-openid_connect-1.8.7/spec/controllers/doorkeeper/ 0000775 0000000 0000000 00000000000 14431346656 0024326 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/controllers/doorkeeper/authorizations_controller_spec.rb 0000664 0000000 0000000 00000034622 14431346656 0033222 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true require 'rails_helper' describe Doorkeeper::AuthorizationsController, type: :controller do let(:user) { create :user } let(:application) { create :application, scopes: default_scopes } let(:default_scopes) { 'openid profile' } let(:token_attributes) { { application_id: application.id, resource_owner_id: user.id, scopes: default_scopes } } def authorize!(params = {}) get :new, params: { response_type: 'code', response_mode: '', current_user: user.id, client_id: application.uid, scope: default_scopes, redirect_uri: application.redirect_uri, }.merge(params) end def build_redirect_uri(params = {}, type: 'query') case type when 'query' Doorkeeper::OAuth::Authorization::URIBuilder.uri_with_query(application.redirect_uri, params) when 'fragment' Doorkeeper::OAuth::Authorization::URIBuilder.uri_with_fragment(application.redirect_uri, params) else raise ArgumentError, "Unsupported uri type #{type}" end end def expect_authorization_form! expect(response).to be_successful expect(response).to render_template('doorkeeper/authorizations/new') end def expect_successful_callback! expect(response).to be_redirect expect(response.location).to match(/^#{Regexp.quote application.redirect_uri}\?code=[-\w]+$/) end describe '#authenticate_resource_owner!' do it 'redirects to login form when not logged in' do authorize! current_user: nil expect(response).to redirect_to '/login' end context 'with OIDC requests' do before do expect(controller).to receive(:handle_oidc_prompt_param!) expect(controller).to receive(:handle_oidc_max_age_param!) end it 'renders the authorization form if logged in' do authorize! expect_authorization_form! end end context 'with non-OIDC requests' do before do expect(controller).not_to receive(:handle_oidc_prompt_param!) expect(controller).not_to receive(:handle_oidc_max_age_param!) end it 'when action is not :new' do get :show, params: { response_type: 'code', current_user: user.id, client_id: application.uid, scope: default_scopes, redirect_uri: application.redirect_uri, } expect(response).to render_template('doorkeeper/authorizations/show') end context 'when pre_authorization is invalid' do it 'render error when client_id is missing' do authorize!(client_id: nil) expect(response).to be_successful expect(response).to render_template('doorkeeper/authorizations/error') end it 'render error when response_type is missing' do authorize!(response_type: nil) expect(response).to be_successful expect(response).to render_template('doorkeeper/authorizations/error') end end it 'when openid scope is not present' do authorize!(scope: 'profile') expect_authorization_form! end end end describe '#handle_oidc_prompt_param!' do it 'is ignored when the openid scope is not present' do authorize! scope: 'profile', prompt: 'invalid' expect_authorization_form! end context 'with a prompt=none parameter' do context 'and a matching token' do before do create :access_token, token_attributes end it 'redirects to the callback if logged in' do authorize! prompt: 'none' expect_successful_callback! end context 'when another prompt value is present' do let(:error_params) do { 'error' => 'invalid_request', 'error_description' => 'The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed.', } end let(:request_param) { { prompt: 'none login' } } it 'redirect as the query uri with an invalid_request error' do authorize! request_param expect(response).to redirect_to build_redirect_uri(error_params) end it 'redirect as the fragment style uri when response_type is implicit flow request' do allow(Doorkeeper.configuration).to receive(:grant_flows).and_return(['implicit_oidc']) authorize! request_param.merge(response_type: 'id_token token') expect(response).to redirect_to build_redirect_uri(error_params, type: 'fragment') end it 'set @authorize_response variable and render form_post template and when the form_post response_mode is specified' do allow(Doorkeeper.configuration).to receive(:grant_flows).and_return(['implicit_oidc']) authorize! request_param.merge(response_type: 'id_token token', response_mode: 'form_post') authorize_response = controller.instance_variable_get :@authorize_response expect(authorize_response.body.to_json).to eq(error_params.to_json) expect(response).to render_template(:form_post) end end context 'when not logged in' do let(:error_params) do { 'error' => 'login_required', 'error_description' => 'The authorization server requires end-user authentication', 'state' => 'somestate', } end let(:request_param) { { current_user: nil } } it 'returns a login_required error' do authorize! request_param.merge(prompt: 'none', state: 'somestate') expect(response).to redirect_to build_redirect_uri(error_params) end it 'redirect as the fragment style uri when response_type is implicit flow request' do allow(Doorkeeper.configuration).to receive(:grant_flows).and_return(['implicit_oidc']) authorize! request_param.merge(response_type: 'id_token token', prompt: 'none', state: 'somestate') expect(response).to redirect_to build_redirect_uri(error_params, type: 'fragment') end it 'set @authorize_response variable and render form_post template and when the form_post response_mode is specified' do allow(Doorkeeper.configuration).to receive(:grant_flows).and_return(['implicit_oidc']) authorize! request_param.merge(response_type: 'id_token token', response_mode: 'form_post', prompt: 'none', state: 'somestate') authorize_response = controller.instance_variable_get :@authorize_response expect(authorize_response.body.to_json).to eq(error_params.to_json) expect(response).to render_template(:form_post) end end end context 'and no matching token' do it 'redirects to the callback if skip_authorization is set to true' do allow(controller).to receive(:skip_authorization?).and_return(true) authorize! prompt: 'none' expect_successful_callback! end context 'when not logged in' do let(:error_params) do { 'error' => 'login_required', 'error_description' => 'The authorization server requires end-user authentication', 'state' => 'somestate', } end let(:request_param) { { current_user: nil } } it 'returns the login_required error when not logged in' do authorize! request_param.merge(prompt: 'none', state: 'somestate') expect(response).to redirect_to build_redirect_uri(error_params) end it 'uses the fragment style uris when redirecting an error for implicit flow request' do allow(Doorkeeper.configuration).to receive(:grant_flows).and_return(['implicit_oidc']) authorize! request_param.merge(response_type: 'id_token token', prompt: 'none', state: 'somestate') expect(response).to redirect_to build_redirect_uri(error_params, type: 'fragment') end it 'set @authorize_response variable and render form_post template and when the form_post response_mode is specified' do allow(Doorkeeper.configuration).to receive(:grant_flows).and_return(['implicit_oidc']) authorize! request_param.merge(response_type: 'id_token token', response_mode: 'form_post', prompt: 'none', state: 'somestate') authorize_response = controller.instance_variable_get :@authorize_response expect(authorize_response.body.to_json).to eq(error_params.to_json) expect(response).to render_template(:form_post) end end it 'returns a consent_required error when logged in' do authorize! prompt: 'none' error_params = { 'error' => 'consent_required', 'error_description' => 'The authorization server requires end-user consent', } expect(response).to redirect_to build_redirect_uri(error_params) end end end context 'with a prompt=login parameter' do it 'redirects to the sign in form if not logged in' do authorize! prompt: 'login', current_user: nil expect(response).to redirect_to('/login') end it 'reauthenticates the user if logged in' do authorize! prompt: 'login' expect(response).to redirect_to('/reauthenticate') end end context 'with a prompt=consent parameter' do it 'redirects to the sign in form if not logged in' do authorize! prompt: 'consent', current_user: nil expect(response).to redirect_to('/login') end it 'renders the authorization form even if a matching token is present' do create :access_token, token_attributes authorize! prompt: 'consent' expect_authorization_form! end end context 'with a prompt=select_account parameter' do it 'redirects to the select account screen' do authorize! prompt: 'select_account' expect(response).to redirect_to('/select_account') end end context 'with an unknown prompt parameter' do it 'returns an invalid_request error' do authorize! prompt: 'maybe' error_params = { 'error' => 'invalid_request', 'error_description' => 'The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed.', } expect(response).to redirect_to build_redirect_uri(error_params) end it 'does not redirect to an invalid redirect_uri' do authorize! prompt: 'maybe', redirect_uri: 'https://evilapp.com' expect(response).not_to be_redirect end end end describe '#handle_oidc_max_age_param!' do context 'with an invalid max_age parameter' do it 'renders the authorization form' do %w[0 -1 -23 foobar].each do |max_age| authorize! max_age: max_age expect_authorization_form! end end end context 'with a max_age=10 parameter' do it 'renders the authorization form if the users last login was within 10 seconds' do user.update! current_sign_in_at: 5.seconds.ago authorize! max_age: 10 expect_authorization_form! end it 'reauthenticates the user if the last login was longer than 10 seconds ago' do user.update! current_sign_in_at: 5.minutes.ago authorize! max_age: 10 expect(response).to redirect_to '/reauthenticate' end it 'reauthenticates the user if the last login is unknown' do user.update! current_sign_in_at: nil authorize! max_age: 10 expect(response).to redirect_to '/reauthenticate' end end end describe '#reauthenticate_oidc_resource_owner' do let(:performed) { true } before do allow(subject).to receive(:performed?) { performed } allow(subject.request).to receive(:path).and_return('/oauth/authorize') allow(subject.request).to receive(:query_parameters) { { client_id: 'foo', prompt: 'login consent select_account' }.with_indifferent_access } end def reauthenticate! passed_args = nil Doorkeeper::OpenidConnect.configure do reauthenticate_resource_owner do |*args| passed_args = args end end subject.send :reauthenticate_oidc_resource_owner, user passed_args end it 'calls reauthenticate_resource_owner with the current user and the return path' do resource_owner, return_to = reauthenticate! expect(resource_owner).to eq user expect(return_to).to eq '/oauth/authorize?client_id=foo&prompt=consent+select_account' end it 'removes login from the prompt parameter and keeps other values' do _, return_to = reauthenticate! return_params = Rack::Utils.parse_query(URI.parse(return_to).query) expect(return_params['prompt']).to eq 'consent select_account' end context 'with a reauthenticator that does not generate a response' do let(:performed) { false } it 'raises a login_required error' do expect do reauthenticate! end.to raise_error(Doorkeeper::OpenidConnect::Errors::LoginRequired) end end end describe '#select_account_for_resource_owner' do before do allow(subject.request).to receive(:path).and_return('/oauth/authorize') allow(subject.request).to receive(:query_parameters) { { client_id: 'foo', prompt: 'login consent select_account' }.with_indifferent_access } end def select_account! passed_args = nil Doorkeeper::OpenidConnect.configure do select_account_for_resource_owner do |*args| passed_args = args end end subject.send :select_account_for_oidc_resource_owner, user passed_args end it 'calls select_account_for_resource_owner with the current user and the return path' do resource_owner, return_to = select_account! expect(resource_owner).to eq user expect(return_to).to eq '/oauth/authorize?client_id=foo&prompt=login+consent' end it 'removes select_account from the prompt parameter and keeps other values' do _, return_to = select_account! return_params = Rack::Utils.parse_query(URI.parse(return_to).query) expect(return_params['prompt']).to eq 'login consent' end end describe '#pre_auth' do it 'permits nonce parameter' do authorize! nonce: '123456' expect(assigns(:pre_auth).nonce).to eq '123456' end end end authorized_applications_controller_spec.rb 0000664 0000000 0000000 00000000537 14431346656 0035002 0 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/controllers/doorkeeper # frozen_string_literal: true require 'rails_helper' describe Doorkeeper::AuthorizedApplicationsController, type: :controller do let(:access_token) { create :access_token } describe '#index' do it 'does not run the extended #authenticate_resource_owner!' do expect do get :index end.not_to raise_error end end end doorkeeper-openid_connect-1.8.7/spec/controllers/userinfo_controller_spec.rb 0000664 0000000 0000000 00000004367 14431346656 0027635 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true require 'rails_helper' describe Doorkeeper::OpenidConnect::UserinfoController, type: :controller do let(:client) { create :application } let(:user) { create :user, name: 'Joe' } let(:token) { create :access_token, application: client, resource_owner_id: user.id } describe '#show' do context 'with a valid access token authorized for the openid scope' do let(:token) { create :access_token, application: client, resource_owner_id: user.id, scopes: 'openid' } it 'returns the basic user information as JSON' do get :show, params: { access_token: token.token } expect(response.status).to eq 200 expect(JSON.parse(response.body)).to eq({ 'sub' => user.id.to_s, 'variable_name' => 'openid-name', 'created_at' => user.created_at.to_i, 'token_id' => token.id, 'both_responses' => 'both', 'user_info_response' => 'user_info', }) end end context 'with a valid access token authorized for the openid and profile scopes' do let(:token) { create :access_token, application: client, resource_owner_id: user.id, scopes: 'openid profile' } it 'returns the full user information as JSON' do get :show, params: { access_token: token.token } expect(response.status).to eq 200 expect(JSON.parse(response.body)).to eq({ 'sub' => user.id.to_s, 'name' => 'Joe', 'variable_name' => 'profile-name', 'created_at' => user.created_at.to_i, 'updated_at' => user.updated_at.to_i, 'token_id' => token.id, 'both_responses' => 'both', 'user_info_response' => 'user_info', }) end end context 'with a valid access token not authorized for the openid scope' do it 'returns an error' do get :show, params: { access_token: token.token } expect(response.status).to eq 403 end end context 'without a valid access token' do it 'returns an error' do get :show, params: { access_token: 'foobar' } expect(response.status).to eq 401 end end end end doorkeeper-openid_connect-1.8.7/spec/dummy/ 0000775 0000000 0000000 00000000000 14431346656 0020754 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/.rspec 0000664 0000000 0000000 00000000036 14431346656 0022070 0 ustar 00root root 0000000 0000000 --color --require spec_helper doorkeeper-openid_connect-1.8.7/spec/dummy/Rakefile 0000664 0000000 0000000 00000000402 14431346656 0022415 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true # Add your own tasks in files placed in lib/tasks ending in .rake, # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. require_relative 'config/application' Rails.application.load_tasks doorkeeper-openid_connect-1.8.7/spec/dummy/app/ 0000775 0000000 0000000 00000000000 14431346656 0021534 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/app/assets/ 0000775 0000000 0000000 00000000000 14431346656 0023036 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/app/assets/config/ 0000775 0000000 0000000 00000000000 14431346656 0024303 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/app/assets/config/manifest.js 0000664 0000000 0000000 00000000100 14431346656 0026436 0 ustar 00root root 0000000 0000000 //= link_tree ../images //= link_directory ../stylesheets .css doorkeeper-openid_connect-1.8.7/spec/dummy/app/assets/images/ 0000775 0000000 0000000 00000000000 14431346656 0024303 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/app/assets/images/.keep 0000664 0000000 0000000 00000000000 14431346656 0025216 0 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/app/assets/javascripts/ 0000775 0000000 0000000 00000000000 14431346656 0025367 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/app/assets/javascripts/application.js 0000664 0000000 0000000 00000001245 14431346656 0030232 0 ustar 00root root 0000000 0000000 // This is a manifest file that'll be compiled into application.js, which will include all the files // listed below. // // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. // // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the // compiled file. JavaScript code in this file should be added after the last require_* statement. // // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details // about supported directives. // //= require_tree . doorkeeper-openid_connect-1.8.7/spec/dummy/app/assets/stylesheets/ 0000775 0000000 0000000 00000000000 14431346656 0025412 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/app/assets/stylesheets/application.css 0000664 0000000 0000000 00000001340 14431346656 0030425 0 ustar 00root root 0000000 0000000 /* * This is a manifest file that'll be compiled into application.css, which will include all the files * listed below. * * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. * * You're free to add application-wide styles to this file and they'll appear at the bottom of the * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS * files in this directory. Styles in this file should be added after the last require_* statement. * It is generally better to create a new file per style scope. * *= require_tree . *= require_self */ doorkeeper-openid_connect-1.8.7/spec/dummy/app/channels/ 0000775 0000000 0000000 00000000000 14431346656 0023327 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/app/channels/application_cable/ 0000775 0000000 0000000 00000000000 14431346656 0026760 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/app/channels/application_cable/channel.rb 0000664 0000000 0000000 00000000156 14431346656 0030717 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module ApplicationCable class Channel < ActionCable::Channel::Base end end doorkeeper-openid_connect-1.8.7/spec/dummy/app/channels/application_cable/connection.rb 0000664 0000000 0000000 00000000164 14431346656 0031445 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module ApplicationCable class Connection < ActionCable::Connection::Base end end doorkeeper-openid_connect-1.8.7/spec/dummy/app/controllers/ 0000775 0000000 0000000 00000000000 14431346656 0024102 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/app/controllers/application_controller.rb 0000664 0000000 0000000 00000000200 14431346656 0031165 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true class ApplicationController < ActionController::Base protect_from_forgery with: :exception end doorkeeper-openid_connect-1.8.7/spec/dummy/app/controllers/concerns/ 0000775 0000000 0000000 00000000000 14431346656 0025714 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/app/controllers/concerns/.keep 0000664 0000000 0000000 00000000000 14431346656 0026627 0 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/app/helpers/ 0000775 0000000 0000000 00000000000 14431346656 0023176 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/app/helpers/application_helper.rb 0000664 0000000 0000000 00000000074 14431346656 0027366 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true module ApplicationHelper end doorkeeper-openid_connect-1.8.7/spec/dummy/app/jobs/ 0000775 0000000 0000000 00000000000 14431346656 0022471 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/app/jobs/application_job.rb 0000664 0000000 0000000 00000000112 14431346656 0026145 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true class ApplicationJob < ActiveJob::Base end doorkeeper-openid_connect-1.8.7/spec/dummy/app/mailers/ 0000775 0000000 0000000 00000000000 14431346656 0023170 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/app/mailers/application_mailer.rb 0000664 0000000 0000000 00000000205 14431346656 0027346 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true class ApplicationMailer < ActionMailer::Base default from: 'from@example.com' layout 'mailer' end doorkeeper-openid_connect-1.8.7/spec/dummy/app/models/ 0000775 0000000 0000000 00000000000 14431346656 0023017 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/app/models/application_record.rb 0000664 0000000 0000000 00000000155 14431346656 0027206 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true class ApplicationRecord < ActiveRecord::Base self.abstract_class = true end doorkeeper-openid_connect-1.8.7/spec/dummy/app/models/concerns/ 0000775 0000000 0000000 00000000000 14431346656 0024631 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/app/models/concerns/.keep 0000664 0000000 0000000 00000000000 14431346656 0025544 0 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/app/models/user.rb 0000664 0000000 0000000 00000000247 14431346656 0024325 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true class User < ActiveRecord::Base def self.authenticate!(name, password) User.where(name: name, password: password).first end end doorkeeper-openid_connect-1.8.7/spec/dummy/bin/ 0000775 0000000 0000000 00000000000 14431346656 0021524 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/bin/bundle 0000775 0000000 0000000 00000000234 14431346656 0022722 0 ustar 00root root 0000000 0000000 #!/usr/bin/env ruby # frozen_string_literal: true ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) load Gem.bin_path('bundler', 'bundle') doorkeeper-openid_connect-1.8.7/spec/dummy/bin/rails 0000775 0000000 0000000 00000000254 14431346656 0022565 0 ustar 00root root 0000000 0000000 #!/usr/bin/env ruby # frozen_string_literal: true APP_PATH = File.expand_path('../config/application', __dir__) require_relative '../config/boot' require 'rails/commands' doorkeeper-openid_connect-1.8.7/spec/dummy/bin/rake 0000775 0000000 0000000 00000000171 14431346656 0022373 0 ustar 00root root 0000000 0000000 #!/usr/bin/env ruby # frozen_string_literal: true require_relative '../config/boot' require 'rake' Rake.application.run doorkeeper-openid_connect-1.8.7/spec/dummy/bin/setup 0000775 0000000 0000000 00000001662 14431346656 0022617 0 ustar 00root root 0000000 0000000 #!/usr/bin/env ruby # frozen_string_literal: true require 'pathname' require 'fileutils' include FileUtils # path to your application root. APP_ROOT = Pathname.new File.expand_path('..', __dir__) def system!(*args) system(*args) || abort("\n== Command #{args} failed ==") end chdir APP_ROOT do # This script is a starting point to setup your application. # Add necessary setup steps to this file. puts '== Installing dependencies ==' system! 'gem install bundler --conservative' system('bundle check') || system!('bundle install') # puts "\n== Copying sample files ==" # unless File.exist?('config/database.yml') # cp 'config/database.yml.sample', 'config/database.yml' # end puts "\n== Preparing database ==" system! 'bin/rails db:setup' puts "\n== Removing old logs and tempfiles ==" system! 'bin/rails log:clear tmp:clear' puts "\n== Restarting application server ==" system! 'bin/rails restart' end doorkeeper-openid_connect-1.8.7/spec/dummy/bin/update 0000775 0000000 0000000 00000001450 14431346656 0022734 0 ustar 00root root 0000000 0000000 #!/usr/bin/env ruby # frozen_string_literal: true require 'pathname' require 'fileutils' include FileUtils # path to your application root. APP_ROOT = Pathname.new File.expand_path('..', __dir__) def system!(*args) system(*args) || abort("\n== Command #{args} failed ==") end chdir APP_ROOT do # This script is a way to update your development environment automatically. # Add necessary update steps to this file. puts '== Installing dependencies ==' system! 'gem install bundler --conservative' system('bundle check') || system!('bundle install') puts "\n== Updating database ==" system! 'bin/rails db:migrate' puts "\n== Removing old logs and tempfiles ==" system! 'bin/rails log:clear tmp:clear' puts "\n== Restarting application server ==" system! 'bin/rails restart' end doorkeeper-openid_connect-1.8.7/spec/dummy/config.ru 0000664 0000000 0000000 00000000241 14431346656 0022566 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true # This file is used by Rack-based servers to start the application. require_relative 'config/environment' run Rails.application doorkeeper-openid_connect-1.8.7/spec/dummy/config/ 0000775 0000000 0000000 00000000000 14431346656 0022221 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/config/application.rb 0000664 0000000 0000000 00000000736 14431346656 0025057 0 ustar 00root root 0000000 0000000 require_relative 'boot' # Pick the frameworks you want: require "active_record/railtie" require "action_controller/railtie" require "action_view/railtie" Bundler.require(*Rails.groups) module Dummy class Application < Rails::Application # Settings in config/environments/* take precedence over those specified here. # Application configuration should go into files in config/initializers # -- all .rb files in that directory are automatically loaded. end end doorkeeper-openid_connect-1.8.7/spec/dummy/config/boot.rb 0000664 0000000 0000000 00000000351 14431346656 0023510 0 ustar 00root root 0000000 0000000 # Set up gems listed in the Gemfile. ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../Gemfile', __dir__) require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) $LOAD_PATH.unshift File.expand_path('../../../lib', __dir__) doorkeeper-openid_connect-1.8.7/spec/dummy/config/database.yml 0000664 0000000 0000000 00000001102 14431346656 0024502 0 ustar 00root root 0000000 0000000 # SQLite version 3.x # gem install sqlite3 # # Ensure the SQLite 3 gem is defined in your Gemfile # gem 'sqlite3' # development: adapter: sqlite3 pool: 5 timeout: 5000 database: db/development.sqlite3 # Warning: The database defined as "test" will be erased and # re-generated from your development database when you run "rake". # Do not set this db to the same as development or production. test: adapter: sqlite3 pool: 5 timeout: 5000 database: db/test.sqlite3 production: adapter: sqlite3 pool: 5 timeout: 5000 database: db/production.sqlite3 doorkeeper-openid_connect-1.8.7/spec/dummy/config/environment.rb 0000664 0000000 0000000 00000000200 14431346656 0025102 0 ustar 00root root 0000000 0000000 # Load the Rails application. require_relative 'application' # Initialize the Rails application. Rails.application.initialize! doorkeeper-openid_connect-1.8.7/spec/dummy/config/environments/ 0000775 0000000 0000000 00000000000 14431346656 0024750 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/config/environments/test.rb 0000664 0000000 0000000 00000002431 14431346656 0026254 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. # The test environment is used exclusively to run your application's # test suite. You never need to work with it otherwise. Remember that # your test database is "scratch space" for the test suite and is wiped # and recreated between test runs. Don't rely on the data there! config.cache_classes = true # Do not eager load code on boot. This avoids loading your whole application # just for the purpose of running a single test. If you are using a tool that # preloads Rails for running tests, you may have to set it to true. config.eager_load = false # Show full error reports and disable caching. config.consider_all_requests_local = true config.action_controller.perform_caching = false # Raise exceptions instead of rendering exception templates. config.action_dispatch.show_exceptions = false # Disable request forgery protection in test environment. config.action_controller.allow_forgery_protection = false # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr # Raises error for missing translations # config.action_view.raise_on_missing_translations = true end doorkeeper-openid_connect-1.8.7/spec/dummy/config/initializers/ 0000775 0000000 0000000 00000000000 14431346656 0024727 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/config/initializers/doorkeeper.rb 0000664 0000000 0000000 00000000511 14431346656 0027410 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true Doorkeeper.configure do optional_scopes :openid resource_owner_authenticator do if params[:current_user].present? User.find(params[:current_user]) else redirect_to('/login') nil end end grant_flows %w[authorization_code client_credentials implicit_oidc] end doorkeeper-openid_connect-1.8.7/spec/dummy/config/initializers/doorkeeper_openid_connect.rb 0000664 0000000 0000000 00000006065 14431346656 0032471 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true Doorkeeper::OpenidConnect.configure do issuer 'dummy' signing_key <<~EOL -----BEGIN RSA PRIVATE KEY----- MIIEpgIBAAKCAQEAsjdnSA6UWUQQHf6BLIkIEUhMRNBJC1NN/pFt1EJmEiI88GS0 ceROO5B5Ooo9Y3QOWJ/n+u1uwTHBz0HCTN4wgArWd1TcqB5GQzQRP4eYnWyPfi4C feqAHzQp+v4VwbcK0LW4FqtW5D0dtrFtI281FDxLhARzkhU2y7fuYhL8fVw5rUhE 8uwvHRZ5CEZyxf7BSHxIvOZAAymhuzNLATt2DGkDInU1BmF75tEtBJAVLzWG/j4L PZh1EpSdfezqaXQlcy9PJi916UzTl0P7Yy+ulOdUsMlB6yo8qKTY1+AbZ5jzneHb GDU/O8QjYvii1WDmJ60t0jXicmOkGrOhruOptwIDAQABAoIBAQChYNwMeu9IugJi NsEf4+JDTBWMRpOuRrwcpfIvQAUPrKNEB90COPvCoju0j9OxCDmpdPtq1K/zD6xx khlw485FVAsKufSp4+g6GJ75yT6gZtq1JtKo1L06BFFzb7uh069eeP7+wB6JxPHw KlAqwxvsfADhxeolQUKCTMb3Vjv/Aw2cO/nn6RAOeftw2aDmFy8Xl+oTUtSxyib0 YCdU9cK8MxsxDdmowwHp04xRTm/wfG5hLEn7HMz1PP86iP9BiFsCqTId9dxEUTS1 K+VAt9FbxRAq5JlBocxUMHNxLigb94Ca2FOMR7F6l/tronLfHD801YoObF0fN9qW Cgw4aTO5AoGBAOR79hiZVM7/l1cBid7hKSeMWKUZ/nrwJsVfNpu1H9xt9uDu+79U mcGfM7pm7L2qCNGg7eeWBHq2CVg/XQacRNtcTlomFrw4tDXUkFN1hE56t1iaTs9m dN9IDr6jFgf6UaoOxxoPT9Q1ZtO46l043Nzrkoz8cBEBaBY20bUDwCYjAoGBAMet tt1ImGF1cx153KbOfjl8v54VYUVkmRNZTa1E821nL/EMpoONSqJmRVsX7grLyPL1 QyZe245NOvn63YM0ng0rn2osoKsMVJwYBEYjHL61iF6dPtW5p8FIs7auRnC3NrG0 XxHATZ4xhHD0iIn14iXh0XIhUVk+nGktHU1gbmVdAoGBANniwKdqqS6RHKBTDkgm Dhnxw6MGa+CO3VpA1xGboxuRHeoY3KfzpIC5MhojBsZDvQ8zWUwMio7+w2CNZEfm g99wYiOjyPCLXocrAssj+Rzh97AdzuQHf5Jh4/W2Dk9jTbdPSl02ltj2Z+2lnJFz pWNjnqimHrSI09rDQi5NulJjAoGBAImquujVpDmNQFCSNA7NTzlTSMk09FtjgCZW 67cKUsqa2fLXRfZs84gD+s1TMks/NMxNTH6n57e0h3TSAOb04AM0kDQjkKJdXfhA lrHEg4z4m4yf3TJ9Tat09HJ+tRIBPzRFp0YVz23Btg4qifiUDdcQWdbWIb/l6vCY qhsu4O4BAoGBANbceYSDYRdT7a5QjJGibkC90Z3vFe4rDTBgZWg7xG0cpSU4JNg7 SFR3PjWQyCg7aGGXiooCM38YQruACTj0IFub24MFRA4ZTXvrACvpsVokJlQiG0Z4 tuQKYki41JvYqPobcq/rLE/AM7PKJftW35nqFuj0MrsUwPacaVwKBf5J -----END RSA PRIVATE KEY----- EOL subject_types_supported [:public] resource_owner_from_access_token do |access_token| User.find_by(id: access_token.resource_owner_id) end auth_time_from_resource_owner do |resource_owner| resource_owner.current_sign_in_at end reauthenticate_resource_owner do |_resource_owner, _return_to| redirect_to '/reauthenticate' end select_account_for_resource_owner do |_resource_owner, _return_to| redirect_to '/select_account' end subject do |resource_owner| resource_owner.id end claims do claim :name do |user| user.name end claim :variable_name, scope: :openid do |user, scopes| scopes.exists?(:profile) ? 'profile-name' : 'openid-name' end claim :created_at, scope: :openid do |user| user.created_at.to_i end claim :updated_at do |user| user.updated_at.to_i end claim :token_id, scope: :openid do |user, scopes, token| token.id end claim(:both_responses, scope: :openid, response: [:id_token, :user_info]) { 'both' } claim(:id_token_response, scope: :openid, response: [:id_token]) { 'id_token' } claim(:user_info_response, scope: :openid, response: :user_info) { 'user_info' } end end doorkeeper-openid_connect-1.8.7/spec/dummy/config/locales/ 0000775 0000000 0000000 00000000000 14431346656 0023643 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/config/locales/doorkeeper_openid_connect.en.yml 0000664 0000000 0000000 00000002745 14431346656 0032205 0 ustar 00root root 0000000 0000000 en: doorkeeper: scopes: openid: 'Authenticate your account' profile: 'View your profile information' email: 'View your email address' address: 'View your physical address' phone: 'View your phone number' errors: messages: login_required: 'The authorization server requires end-user authentication' consent_required: 'The authorization server requires end-user consent' interaction_required: 'The authorization server requires end-user interaction' account_selection_required: 'The authorization server requires end-user account selection' openid_connect: errors: messages: # Configuration error messages resource_owner_from_access_token_not_configured: 'Failure due to Doorkeeper::OpenidConnect.configure.resource_owner_from_access_token missing configuration.' auth_time_from_resource_owner_not_configured: 'Failure due to Doorkeeper::OpenidConnect.configure.auth_time_from_resource_owner missing configuration.' reauthenticate_resource_owner_not_configured: 'Failure due to Doorkeeper::OpenidConnect.configure.reauthenticate_resource_owner missing configuration.' select_account_for_resource_owner_not_configured: 'Failure due to Doorkeeper::OpenidConnect.configure.select_account_for_resource_owner missing configuration.' subject_not_configured: 'ID Token generation failed due to Doorkeeper::OpenidConnect.configure.subject missing configuration.' doorkeeper-openid_connect-1.8.7/spec/dummy/config/routes.rb 0000664 0000000 0000000 00000000154 14431346656 0024067 0 ustar 00root root 0000000 0000000 Rails.application.routes.draw do use_doorkeeper use_doorkeeper_openid_connect root 'dummy#index' end doorkeeper-openid_connect-1.8.7/spec/dummy/config/secrets.yml 0000664 0000000 0000000 00000001705 14431346656 0024417 0 ustar 00root root 0000000 0000000 # Be sure to restart your server when you modify this file. # Your secret key is used for verifying the integrity of signed cookies. # If you change this key, all old signed cookies will become invalid! # Make sure the secret is at least 30 characters and all random, # no regular words or you'll be exposed to dictionary attacks. # You can use `rails secret` to generate a secure secret key. # Make sure the secrets in this file are kept private # if you're sharing your code publicly. development: secret_key_base: 047f8d91118b6fad743ccac15928b306d1880d9d104377eb1f7aeed909ce852c0a214074742163a6e0bc814482c8cdeae470c5edfc75eee37c8da6ccbbae4199 test: secret_key_base: 88becc3eb1c84af38da6deea4627f29f2bd41ba79dd279a0e379a41a82f18316f6f5221c73182adca329df8be13fd7c115804a82246989f1314e93d7efc745d3 # Do not keep production secrets in the repository, # instead read values from the environment. production: secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> doorkeeper-openid_connect-1.8.7/spec/dummy/db/ 0000775 0000000 0000000 00000000000 14431346656 0021341 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/db/migrate/ 0000775 0000000 0000000 00000000000 14431346656 0022771 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/db/migrate/20111122132257_create_users.rb 0000664 0000000 0000000 00000000267 14431346656 0027444 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true class CreateUsers < ActiveRecord::Migration[4.2] def change create_table :users do |t| t.string :name t.timestamps end end end doorkeeper-openid_connect-1.8.7/spec/dummy/db/migrate/20120312140401_add_password_to_users.rb 0000664 0000000 0000000 00000000230 14431346656 0031332 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true class AddPasswordToUsers < ActiveRecord::Migration[4.2] def change add_column :users, :password, :string end end doorkeeper-openid_connect-1.8.7/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb 0000664 0000000 0000000 00000004016 14431346656 0031776 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true class CreateDoorkeeperTables < ActiveRecord::Migration[4.2] def change create_table :oauth_applications do |t| t.string :name, null: false t.string :uid, null: false t.string :secret, null: false t.text :redirect_uri, null: false t.string :scopes, null: false, default: '' t.timestamps null: false end add_index :oauth_applications, :uid, unique: true create_table :oauth_access_grants do |t| t.integer :resource_owner_id, null: false t.references :application, null: false t.string :token, null: false t.integer :expires_in, null: false t.text :redirect_uri, null: false t.datetime :created_at, null: false t.datetime :revoked_at t.string :scopes end add_index :oauth_access_grants, :token, unique: true add_foreign_key( :oauth_access_grants, :oauth_applications, column: :application_id, ) create_table :oauth_access_tokens do |t| t.integer :resource_owner_id t.references :application # If you use a custom token generator you may need to change this column # from string to text, so that it accepts tokens larger than 255 # characters. More info on custom token generators in: # https://github.com/doorkeeper-gem/doorkeeper/tree/v3.0.0.rc1#custom-access-token-generator # # t.text :token, null: false t.string :token, null: false t.string :refresh_token t.integer :expires_in t.datetime :revoked_at t.datetime :created_at, null: false t.string :scopes end add_index :oauth_access_tokens, :token, unique: true add_index :oauth_access_tokens, :resource_owner_id add_index :oauth_access_tokens, :refresh_token, unique: true add_foreign_key( :oauth_access_tokens, :oauth_applications, column: :application_id, ) end end doorkeeper-openid_connect-1.8.7/spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb 0000664 0000000 0000000 00000000466 14431346656 0031774 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true class AddOwnerToApplication < ActiveRecord::Migration[4.2] def change add_column :oauth_applications, :owner_id, :integer, null: true add_column :oauth_applications, :owner_type, :string, null: true add_index :oauth_applications, [:owner_id, :owner_type] end end 20160320211015_add_previous_refresh_token_to_access_tokens.rb 0000664 0000000 0000000 00000000406 14431346656 0035676 0 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/db/migrate # frozen_string_literal: true class AddPreviousRefreshTokenToAccessTokens < ActiveRecord::Migration[4.2] def change add_column( :oauth_access_tokens, :previous_refresh_token, :string, default: '', null: false ) end end 20161102204540_add_current_sign_in_at_to_users.rb 0000664 0000000 0000000 00000000253 14431346656 0033301 0 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/db/migrate # frozen_string_literal: true class AddCurrentSignInAtToUsers < ActiveRecord::Migration[4.2] def change add_column :users, :current_sign_in_at, :datetime end end 20180904152859_add_confidential_to_applications.rb 0000664 0000000 0000000 00000000366 14431346656 0033450 0 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/db/migrate # frozen_string_literal: true class AddConfidentialToApplications < ActiveRecord::Migration[5.0] def change add_column( :oauth_applications, :confidential, :boolean, null: false, default: true ) end end doorkeeper-openid_connect-1.8.7/spec/dummy/db/migrate/20221122044143_enable_pkce.rb 0000664 0000000 0000000 00000000401 14431346656 0027174 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true class EnablePkce < ActiveRecord::Migration[6.0] def change add_column :oauth_access_grants, :code_challenge, :string, null: true add_column :oauth_access_grants, :code_challenge_method, :string, null: true end end doorkeeper-openid_connect-1.8.7/spec/dummy/db/schema.rb 0000664 0000000 0000000 00000006267 14431346656 0023141 0 ustar 00root root 0000000 0000000 # This file is auto-generated from the current state of the database. Instead # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. # # This file is the source Rails uses to define your schema when running `rails # db:schema:load`. When creating a new database, `rails db:schema:load` tends to # be faster and is potentially less error prone than running all of your # migrations from scratch. Old migrations may fail to apply correctly if those # migrations use external dependencies or application code. # # It's strongly recommended that you check this file into your version control system. ActiveRecord::Schema.define(version: 2022_11_22_044143) do create_table "oauth_access_grants", force: :cascade do |t| t.integer "resource_owner_id", null: false t.integer "application_id", null: false t.string "token", null: false t.integer "expires_in", null: false t.text "redirect_uri", null: false t.datetime "created_at", null: false t.datetime "revoked_at" t.string "scopes" t.string "code_challenge" t.string "code_challenge_method" t.index ["token"], name: "index_oauth_access_grants_on_token", unique: true end create_table "oauth_access_tokens", force: :cascade do |t| t.integer "resource_owner_id" t.integer "application_id" t.string "token", null: false t.string "refresh_token" t.integer "expires_in" t.datetime "revoked_at" t.datetime "created_at", null: false t.string "scopes" t.string "previous_refresh_token", default: "", null: false t.index ["refresh_token"], name: "index_oauth_access_tokens_on_refresh_token", unique: true t.index ["resource_owner_id"], name: "index_oauth_access_tokens_on_resource_owner_id" t.index ["token"], name: "index_oauth_access_tokens_on_token", unique: true end create_table "oauth_applications", force: :cascade do |t| t.string "name", null: false t.string "uid", null: false t.string "secret", null: false t.text "redirect_uri", null: false t.string "scopes", default: "", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "owner_id" t.string "owner_type" t.boolean "confidential", default: true, null: false t.index ["owner_id", "owner_type"], name: "index_oauth_applications_on_owner_id_and_owner_type" t.index ["uid"], name: "index_oauth_applications_on_uid", unique: true end create_table "oauth_openid_requests", force: :cascade do |t| t.integer "access_grant_id", null: false t.string "nonce", null: false t.index ["access_grant_id"], name: "index_oauth_openid_requests_on_access_grant_id" end create_table "users", force: :cascade do |t| t.string "name" t.datetime "created_at" t.datetime "updated_at" t.string "password" t.datetime "current_sign_in_at" end add_foreign_key "oauth_access_grants", "oauth_applications", column: "application_id" add_foreign_key "oauth_access_tokens", "oauth_applications", column: "application_id" add_foreign_key "oauth_openid_requests", "oauth_access_grants", column: "access_grant_id", on_delete: :cascade end doorkeeper-openid_connect-1.8.7/spec/dummy/lib/ 0000775 0000000 0000000 00000000000 14431346656 0021522 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/lib/assets/ 0000775 0000000 0000000 00000000000 14431346656 0023024 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/lib/assets/.keep 0000664 0000000 0000000 00000000000 14431346656 0023737 0 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/log/ 0000775 0000000 0000000 00000000000 14431346656 0021535 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/log/.keep 0000664 0000000 0000000 00000000000 14431346656 0022450 0 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/public/ 0000775 0000000 0000000 00000000000 14431346656 0022232 5 ustar 00root root 0000000 0000000 doorkeeper-openid_connect-1.8.7/spec/dummy/public/404.html 0000664 0000000 0000000 00000003034 14431346656 0023427 0 ustar 00root root 0000000 0000000
You may have mistyped the address or the page may have moved.
If you are the application owner check the logs for more information.
Maybe you tried to change something you didn't have access to.
If you are the application owner check the logs for more information.
If you are the application owner check the logs for more information.