pax_global_header00006660000000000000000000000064144313466560014526gustar00rootroot0000000000000052 comment=5987683ccc22262beb6e44c76ca4b65288d6067a doorkeeper-openid_connect-1.8.7/000077500000000000000000000000001443134665600166675ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/.github/000077500000000000000000000000001443134665600202275ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/.github/dependabot.yml000066400000000000000000000003051443134665600230550ustar00rootroot00000000000000version: 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/000077500000000000000000000000001443134665600222645ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/.github/workflows/ci.yml000066400000000000000000000017721443134665600234110ustar00rootroot00000000000000name: 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/.gitignore000066400000000000000000000002711443134665600206570ustar00rootroot00000000000000/.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.yml000066400000000000000000000025331443134665600211440ustar00rootroot00000000000000inherit_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.yml000066400000000000000000000023251443134665600221700ustar00rootroot00000000000000# 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-gemset000066400000000000000000000000321443134665600211260ustar00rootroot00000000000000doorkeeper-openid_connect doorkeeper-openid_connect-1.8.7/CHANGELOG.md000066400000000000000000000230221443134665600204770ustar00rootroot00000000000000## 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.md000066400000000000000000000034111443134665600211170ustar00rootroot00000000000000# 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/Gemfile000066400000000000000000000005311443134665600201610ustar00rootroot00000000000000# 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.txt000066400000000000000000000020571443134665600205160ustar00rootroot00000000000000MIT 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.md000066400000000000000000000361151443134665600201540ustar00rootroot00000000000000# Doorkeeper::OpenidConnect [![Build Status](https://github.com/doorkeeper-gem/doorkeeper-openid_connect/workflows/CI/badge.svg?branch=master)](https://github.com/doorkeeper-gem/doorkeeper-openid_connect/actions) [![Code Climate](https://codeclimate.com/github/doorkeeper-gem/doorkeeper-openid_connect.svg)](https://codeclimate.com/github/doorkeeper-gem/doorkeeper-openid_connect) [![Gem Version](https://badge.fury.io/rb/doorkeeper-openid_connect.svg)](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/Rakefile000066400000000000000000000010241443134665600203310ustar00rootroot00000000000000# 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/000077500000000000000000000000001443134665600174475ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/app/controllers/000077500000000000000000000000001443134665600220155ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/app/controllers/concerns/000077500000000000000000000000001443134665600236275ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/app/controllers/concerns/doorkeeper/000077500000000000000000000000001443134665600257665ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/app/controllers/concerns/doorkeeper/openid_connect/000077500000000000000000000000001443134665600307555ustar00rootroot00000000000000authorizations_extension.rb000066400000000000000000000002651443134665600364050ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/app/controllers/concerns/doorkeeper/openid_connectmodule 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/000077500000000000000000000000001443134665600241545ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/app/controllers/doorkeeper/openid_connect/000077500000000000000000000000001443134665600271435ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/app/controllers/doorkeeper/openid_connect/discovery_controller.rb000066400000000000000000000104021443134665600337370ustar00rootroot00000000000000# 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.rb000066400000000000000000000005211443134665600335630ustar00rootroot00000000000000# 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/000077500000000000000000000000001443134665600174375ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/bin/console000077500000000000000000000002421443134665600210250ustar00rootroot00000000000000#!/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/setup000077500000000000000000000002031443134665600205200ustar00rootroot00000000000000#!/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/000077500000000000000000000000001443134665600201345ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/config/locales/000077500000000000000000000000001443134665600215565ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/config/locales/en.yml000066400000000000000000000027451443134665600227130ustar00rootroot00000000000000en: 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.gemspec000066400000000000000000000024611443134665600253630ustar00rootroot00000000000000# 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/000077500000000000000000000000001443134665600204625ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/gemfiles/doorkeeper_master.gemfile000066400000000000000000000004261443134665600255300ustar00rootroot00000000000000# 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.gemfile000066400000000000000000000003141443134665600235070ustar00rootroot00000000000000# 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.gemfile000066400000000000000000000003141443134665600235100ustar00rootroot00000000000000# 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.gemfile000066400000000000000000000003141443134665600235100ustar00rootroot00000000000000# 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/000077500000000000000000000000001443134665600174355ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/lib/doorkeeper/000077500000000000000000000000001443134665600215745ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/lib/doorkeeper/oauth/000077500000000000000000000000001443134665600227145ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/lib/doorkeeper/oauth/id_token_request.rb000066400000000000000000000014641443134665600266120ustar00rootroot00000000000000# 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.rb000066400000000000000000000012431443134665600267530ustar00rootroot00000000000000# 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.rb000066400000000000000000000005201443134665600300020ustar00rootroot00000000000000# 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.rb000066400000000000000000000004231443134665600301520ustar00rootroot00000000000000# 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.rb000066400000000000000000000045641443134665600251210ustar00rootroot00000000000000# 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/000077500000000000000000000000001443134665600245635ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/claims/000077500000000000000000000000001443134665600260335ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/claims/aggregated_claim.rb000066400000000000000000000002651443134665600316220ustar00rootroot00000000000000# 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.rb000066400000000000000000000022201443134665600274410ustar00rootroot00000000000000# 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.rb000066400000000000000000000003121443134665600320430ustar00rootroot00000000000000# 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.rb000066400000000000000000000005331443134665600310160ustar00rootroot00000000000000# 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.rb000066400000000000000000000020371443134665600300700ustar00rootroot00000000000000# 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.rb000066400000000000000000000055141443134665600263620ustar00rootroot00000000000000# 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.rb000066400000000000000000000006071443134665600263600ustar00rootroot00000000000000# 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.rb000066400000000000000000000017041443134665600264260ustar00rootroot00000000000000# 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/000077500000000000000000000000001443134665600262255ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/helpers/controller.rb000066400000000000000000000117341443134665600307430ustar00rootroot00000000000000# 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.rb000066400000000000000000000036031443134665600267060ustar00rootroot00000000000000# 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.rb000066400000000000000000000024751443134665600301140ustar00rootroot00000000000000# 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/000077500000000000000000000000001443134665600257035ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/oauth/authorization/000077500000000000000000000000001443134665600306035ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/oauth/authorization/code.rb000066400000000000000000000020771443134665600320500ustar00rootroot00000000000000# 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.rb000066400000000000000000000011451443134665600336730ustar00rootroot00000000000000# 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.rb000066400000000000000000000016001443134665600343600ustar00rootroot00000000000000# 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.rb000066400000000000000000000015771443134665600320100ustar00rootroot00000000000000# 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.rb000066400000000000000000000010761443134665600312720ustar00rootroot00000000000000# 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/000077500000000000000000000000001443134665600253605ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/orm/active_record.rb000066400000000000000000000037301443134665600305210ustar00rootroot00000000000000# 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/000077500000000000000000000000001443134665600301715ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/orm/active_record/access_grant.rb000066400000000000000000000006241443134665600331540ustar00rootroot00000000000000# 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.rb000066400000000000000000000012501443134665600322040ustar00rootroot00000000000000# 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/000077500000000000000000000000001443134665600256755ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/rails/routes.rb000066400000000000000000000036371443134665600275540ustar00rootroot00000000000000# 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/000077500000000000000000000000001443134665600272165ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/lib/doorkeeper/openid_connect/rails/routes/mapper.rb000066400000000000000000000012661443134665600310340ustar00rootroot00000000000000# 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.rb000066400000000000000000000014341443134665600312000ustar00rootroot00000000000000# 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.rb000066400000000000000000000015021443134665600270770ustar00rootroot00000000000000# 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.rb000066400000000000000000000001501443134665600265710ustar00rootroot00000000000000# frozen_string_literal: true module Doorkeeper module OpenidConnect VERSION = '1.8.7' end end doorkeeper-openid_connect-1.8.7/lib/doorkeeper/request/000077500000000000000000000000001443134665600232645ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/lib/doorkeeper/request/id_token.rb000066400000000000000000000005771443134665600254160ustar00rootroot00000000000000# 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.rb000066400000000000000000000006111443134665600266030ustar00rootroot00000000000000# 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/000077500000000000000000000000001443134665600216065ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/lib/generators/doorkeeper/000077500000000000000000000000001443134665600237455ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/lib/generators/doorkeeper/openid_connect/000077500000000000000000000000001443134665600267345ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/lib/generators/doorkeeper/openid_connect/install_generator.rb000066400000000000000000000011151443134665600327730ustar00rootroot00000000000000# 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.rb000066400000000000000000000015621443134665600333240ustar00rootroot00000000000000# 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/000077500000000000000000000000001443134665600307325ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/lib/generators/doorkeeper/openid_connect/templates/initializer.rb000066400000000000000000000041441443134665600336050ustar00rootroot00000000000000# 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.erb000066400000000000000000000006301443134665600340160ustar00rootroot00000000000000class 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/000077500000000000000000000000001443134665600176215ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/controllers/000077500000000000000000000000001443134665600221675ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/controllers/discovery_controller_spec.rb000066400000000000000000000240251443134665600300030ustar00rootroot00000000000000# 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/000077500000000000000000000000001443134665600243265ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/controllers/doorkeeper/authorizations_controller_spec.rb000066400000000000000000000346221443134665600332220ustar00rootroot00000000000000# 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.rb000066400000000000000000000005371443134665600350020ustar00rootroot00000000000000doorkeeper-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.rb000066400000000000000000000043671443134665600276350ustar00rootroot00000000000000# 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/000077500000000000000000000000001443134665600207545ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/.rspec000066400000000000000000000000361443134665600220700ustar00rootroot00000000000000--color --require spec_helper doorkeeper-openid_connect-1.8.7/spec/dummy/Rakefile000066400000000000000000000004021443134665600224150ustar00rootroot00000000000000# 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/000077500000000000000000000000001443134665600215345ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/app/assets/000077500000000000000000000000001443134665600230365ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/app/assets/config/000077500000000000000000000000001443134665600243035ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/app/assets/config/manifest.js000066400000000000000000000001001443134665600264360ustar00rootroot00000000000000 //= link_tree ../images //= link_directory ../stylesheets .css doorkeeper-openid_connect-1.8.7/spec/dummy/app/assets/images/000077500000000000000000000000001443134665600243035ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/app/assets/images/.keep000066400000000000000000000000001443134665600252160ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/app/assets/javascripts/000077500000000000000000000000001443134665600253675ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/app/assets/javascripts/application.js000066400000000000000000000012451443134665600302320ustar00rootroot00000000000000// 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/000077500000000000000000000000001443134665600254125ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/app/assets/stylesheets/application.css000066400000000000000000000013401443134665600304250ustar00rootroot00000000000000/* * 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/000077500000000000000000000000001443134665600233275ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/app/channels/application_cable/000077500000000000000000000000001443134665600267605ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/app/channels/application_cable/channel.rb000066400000000000000000000001561443134665600307170ustar00rootroot00000000000000# 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.rb000066400000000000000000000001641443134665600314450ustar00rootroot00000000000000# frozen_string_literal: true module ApplicationCable class Connection < ActionCable::Connection::Base end end doorkeeper-openid_connect-1.8.7/spec/dummy/app/controllers/000077500000000000000000000000001443134665600241025ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/app/controllers/application_controller.rb000066400000000000000000000002001443134665600311650ustar00rootroot00000000000000# 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/000077500000000000000000000000001443134665600257145ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/app/controllers/concerns/.keep000066400000000000000000000000001443134665600266270ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/app/helpers/000077500000000000000000000000001443134665600231765ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/app/helpers/application_helper.rb000066400000000000000000000000741443134665600273660ustar00rootroot00000000000000# frozen_string_literal: true module ApplicationHelper end doorkeeper-openid_connect-1.8.7/spec/dummy/app/jobs/000077500000000000000000000000001443134665600224715ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/app/jobs/application_job.rb000066400000000000000000000001121443134665600261450ustar00rootroot00000000000000# frozen_string_literal: true class ApplicationJob < ActiveJob::Base end doorkeeper-openid_connect-1.8.7/spec/dummy/app/mailers/000077500000000000000000000000001443134665600231705ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/app/mailers/application_mailer.rb000066400000000000000000000002051443134665600273460ustar00rootroot00000000000000# 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/000077500000000000000000000000001443134665600230175ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/app/models/application_record.rb000066400000000000000000000001551443134665600272060ustar00rootroot00000000000000# frozen_string_literal: true class ApplicationRecord < ActiveRecord::Base self.abstract_class = true end doorkeeper-openid_connect-1.8.7/spec/dummy/app/models/concerns/000077500000000000000000000000001443134665600246315ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/app/models/concerns/.keep000066400000000000000000000000001443134665600255440ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/app/models/user.rb000066400000000000000000000002471443134665600243250ustar00rootroot00000000000000# 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/000077500000000000000000000000001443134665600215245ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/bin/bundle000077500000000000000000000002341443134665600227220ustar00rootroot00000000000000#!/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/rails000077500000000000000000000002541443134665600225650ustar00rootroot00000000000000#!/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/rake000077500000000000000000000001711443134665600223730ustar00rootroot00000000000000#!/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/setup000077500000000000000000000016621443134665600226170ustar00rootroot00000000000000#!/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/update000077500000000000000000000014501443134665600227340ustar00rootroot00000000000000#!/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.ru000066400000000000000000000002411443134665600225660ustar00rootroot00000000000000# 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/000077500000000000000000000000001443134665600222215ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/config/application.rb000066400000000000000000000007361443134665600250570ustar00rootroot00000000000000require_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.rb000066400000000000000000000003511443134665600235100ustar00rootroot00000000000000# 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.yml000066400000000000000000000011021443134665600245020ustar00rootroot00000000000000# 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.rb000066400000000000000000000002001443134665600251020ustar00rootroot00000000000000# Load the Rails application. require_relative 'application' # Initialize the Rails application. Rails.application.initialize! doorkeeper-openid_connect-1.8.7/spec/dummy/config/environments/000077500000000000000000000000001443134665600247505ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/config/environments/test.rb000066400000000000000000000024311443134665600262540ustar00rootroot00000000000000# 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/000077500000000000000000000000001443134665600247275ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/config/initializers/doorkeeper.rb000066400000000000000000000005111443134665600274100ustar00rootroot00000000000000# 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.rb000066400000000000000000000060651443134665600324710ustar00rootroot00000000000000# 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/000077500000000000000000000000001443134665600236435ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/config/locales/doorkeeper_openid_connect.en.yml000066400000000000000000000027451443134665600322050ustar00rootroot00000000000000en: 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.rb000066400000000000000000000001541443134665600240670ustar00rootroot00000000000000Rails.application.routes.draw do use_doorkeeper use_doorkeeper_openid_connect root 'dummy#index' end doorkeeper-openid_connect-1.8.7/spec/dummy/config/secrets.yml000066400000000000000000000017051443134665600244170ustar00rootroot00000000000000# 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/000077500000000000000000000000001443134665600213415ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/db/migrate/000077500000000000000000000000001443134665600227715ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/db/migrate/20111122132257_create_users.rb000066400000000000000000000002671443134665600274440ustar00rootroot00000000000000# 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.rb000066400000000000000000000002301443134665600313320ustar00rootroot00000000000000# 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.rb000066400000000000000000000040161443134665600317760ustar00rootroot00000000000000# 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.rb000066400000000000000000000004661443134665600317740ustar00rootroot00000000000000# 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.rb000066400000000000000000000004061443134665600356760ustar00rootroot00000000000000doorkeeper-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.rb000066400000000000000000000002531443134665600333010ustar00rootroot00000000000000doorkeeper-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.rb000066400000000000000000000003661443134665600334500ustar00rootroot00000000000000doorkeeper-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.rb000066400000000000000000000004011443134665600271740ustar00rootroot00000000000000# 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.rb000066400000000000000000000062671443134665600231410ustar00rootroot00000000000000# 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/000077500000000000000000000000001443134665600215225ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/lib/assets/000077500000000000000000000000001443134665600230245ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/lib/assets/.keep000066400000000000000000000000001443134665600237370ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/log/000077500000000000000000000000001443134665600215355ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/log/.keep000066400000000000000000000000001443134665600224500ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/public/000077500000000000000000000000001443134665600222325ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/dummy/public/404.html000066400000000000000000000030341443134665600234270ustar00rootroot00000000000000 The page you were looking for doesn't exist (404)

The page you were looking for doesn't exist.

You may have mistyped the address or the page may have moved.

If you are the application owner check the logs for more information.

doorkeeper-openid_connect-1.8.7/spec/dummy/public/422.html000066400000000000000000000030131443134665600234240ustar00rootroot00000000000000 The change you wanted was rejected (422)

The change you wanted was rejected.

Maybe you tried to change something you didn't have access to.

If you are the application owner check the logs for more information.

doorkeeper-openid_connect-1.8.7/spec/dummy/public/500.html000066400000000000000000000027051443134665600234300ustar00rootroot00000000000000 We're sorry, but something went wrong (500)

We're sorry, but something went wrong.

If you are the application owner check the logs for more information.

doorkeeper-openid_connect-1.8.7/spec/dummy/public/favicon.ico000066400000000000000000000000001443134665600243410ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/factories.rb000066400000000000000000000015471443134665600221340ustar00rootroot00000000000000# frozen_string_literal: true FactoryBot.define do factory :access_grant, class: 'Doorkeeper::AccessGrant' do resource_owner_id { create(:user).id } application redirect_uri { 'https://app.com/callback' } expires_in { 100 } scopes { 'public write' } end factory :access_token, class: 'Doorkeeper::AccessToken' do resource_owner_id { create(:user).id } application expires_in { 2.hours } factory :clientless_access_token do application { nil } end end factory :application, class: 'Doorkeeper::Application' do sequence(:name) { |n| "Application #{n}" } redirect_uri { 'https://app.com/callback' } end factory :user do current_sign_in_at { Time.zone.at(23) } end factory :openid_request, class: 'Doorkeeper::OpenidConnect::Request' do access_grant sequence(:nonce, &:to_s) end end doorkeeper-openid_connect-1.8.7/spec/lib/000077500000000000000000000000001443134665600203675ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/lib/claims/000077500000000000000000000000001443134665600216375ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/lib/claims/claim_spec.rb000066400000000000000000000022401443134665600242610ustar00rootroot00000000000000# frozen_string_literal: true require 'rails_helper' describe Doorkeeper::OpenidConnect::Claims::Claim do subject { described_class.new name: 'username', scope: 'profile' } describe '#initialize' do it 'uses the given name' do expect(subject.name).to eq :username end it 'uses the given scope' do expect(subject.scope).to eq :profile end it 'falls back to the default scope for standard claims' do expect(described_class.new(name: 'family_name').scope).to eq :profile expect(described_class.new(name: :family_name).scope).to eq :profile expect(described_class.new(name: 'email').scope).to eq :email expect(described_class.new(name: :email).scope).to eq :email expect(described_class.new(name: 'address').scope).to eq :address expect(described_class.new(name: :address).scope).to eq :address expect(described_class.new(name: 'phone_number').scope).to eq :phone expect(described_class.new(name: :phone_number).scope).to eq :phone end it 'falls back to the profile scope for non-standard claims' do expect(described_class.new(name: 'unknown').scope).to eq :profile end end end doorkeeper-openid_connect-1.8.7/spec/lib/config_spec.rb000066400000000000000000000145041443134665600231770ustar00rootroot00000000000000# frozen_string_literal: true require 'rails_helper' describe Doorkeeper::OpenidConnect, 'configuration' do subject { described_class.configuration } describe '#configure' do it 'fails if not set to :active_record' do # stub ORM setup to avoid Doorkeeper exceptions allow(Doorkeeper).to receive(:setup_orm_adapter) allow(Doorkeeper).to receive(:setup_orm_models) allow(Doorkeeper).to receive(:setup_application_owner) Doorkeeper.configure do orm :mongoid end expect do described_class.configure {} end.to raise_error Doorkeeper::OpenidConnect::Errors::InvalidConfiguration end end describe 'jws_private_key' do it 'delegates to signing_key' do value = 'private_key' described_class.configure do jws_private_key value end expect(subject.signing_key).to eq(value) end end describe 'signing_key' do it 'sets the value that is accessible via signing_key' do value = 'private_key' described_class.configure do signing_key value end expect(subject.signing_key).to eq(value) end end describe 'issuer' do it 'sets the value that is accessible via issuer' do value = 'issuer' described_class.configure do issuer value end expect(subject.issuer).to eq(value) end it 'sets the block that is accessible via issuer' do block = proc {} described_class.configure do issuer(&block) end expect(subject.issuer).to eq(block) end end describe 'resource_owner_from_access_token' do it 'sets the block that is accessible via resource_owner_from_access_token' do block = proc {} described_class.configure do resource_owner_from_access_token(&block) end expect(subject.resource_owner_from_access_token).to eq(block) end it 'fails if unset' do described_class.configure {} expect do subject.resource_owner_from_access_token.call end.to raise_error Doorkeeper::OpenidConnect::Errors::InvalidConfiguration end end describe 'auth_time_from_resource_owner' do it 'sets the block that is accessible via auth_time_from_resource_owner' do block = proc {} described_class.configure do auth_time_from_resource_owner(&block) end expect(subject.auth_time_from_resource_owner).to eq(block) end it 'fails if unset' do described_class.configure {} expect do subject.auth_time_from_resource_owner.call end.to raise_error Doorkeeper::OpenidConnect::Errors::InvalidConfiguration end end describe 'reauthenticate_resource_owner' do it 'sets the block that is accessible via reauthenticate_resource_owner' do block = proc {} described_class.configure do reauthenticate_resource_owner(&block) end expect(subject.reauthenticate_resource_owner).to eq(block) end it 'fails if unset' do described_class.configure {} expect do subject.reauthenticate_resource_owner.call end.to raise_error Doorkeeper::OpenidConnect::Errors::InvalidConfiguration end end describe 'select_account_for_resource_owner' do it 'sets the block that is accessible via select_account_for_resource_owner' do block = proc {} described_class.configure do select_account_for_resource_owner(&block) end expect(subject.select_account_for_resource_owner).to eq(block) end it 'fails if unset' do described_class.configure {} expect do subject.select_account_for_resource_owner.call end.to raise_error Doorkeeper::OpenidConnect::Errors::InvalidConfiguration end end describe 'subject' do it 'sets the block that is accessible via subject' do block = proc {} described_class.configure do subject(&block) end expect(subject.subject).to eq(block) end it 'fails if unset' do described_class.configure {} expect do subject.subject.call end.to raise_error Doorkeeper::OpenidConnect::Errors::InvalidConfiguration end end describe 'expiration' do it 'sets the value that is accessible via expiration' do value = 'expiration' described_class.configure do expiration value end expect(subject.expiration).to eq(value) end end describe 'claims' do it 'sets the claims configuration that is accessible via claims' do described_class.configure do claims do end end expect(subject.claims).not_to be_nil end end describe 'protocol' do it 'defaults to https in production' do expect(::Rails.env).to receive(:production?).and_return(true) expect(subject.protocol.call).to eq(:https) end it 'defaults to http in other environments' do expect(::Rails.env).to receive(:production?).and_return(false) expect(subject.protocol.call).to eq(:http) end it 'can be set to other protocols' do described_class.configure do protocol { :ftp } end expect(subject.protocol.call).to eq(:ftp) end end describe 'end_session_endpoint' do it 'defaults to nil' do expect(subject.end_session_endpoint.call).to be_nil end it 'can be set to a custom url' do described_class.configure do end_session_endpoint { 'http://test.host/logout' } end expect(subject.end_session_endpoint.call).to eq('http://test.host/logout') end end describe 'discovery_url_options' do it 'defaults to empty hash' do expect(subject.discovery_url_options.call).to be_kind_of(Hash) expect(subject.discovery_url_options.call).to be_empty end it 'can be set to other hosts' 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 expect(subject.discovery_url_options.call[:authorization]).to eq(host: 'alternate-authorization-host') end end end doorkeeper-openid_connect-1.8.7/spec/lib/id_token_spec.rb000066400000000000000000000051261443134665600235260ustar00rootroot00000000000000# frozen_string_literal: true require 'rails_helper' describe Doorkeeper::OpenidConnect::IdToken do subject { described_class.new(access_token, nonce) } let(:access_token) { create :access_token, resource_owner_id: user.id, scopes: 'openid' } let(:user) { create :user } let(:nonce) { '123456' } before do allow(Time).to receive(:now) { Time.zone.at 60 } end describe '#nonce' do it 'returns the stored nonce' do expect(subject.nonce).to eq '123456' end end describe '#claims' do it 'returns all default claims' do expect(subject.claims).to eq( iss: 'dummy', sub: user.id.to_s, aud: access_token.application.uid, exp: 180, iat: 60, nonce: nonce, auth_time: 23, both_responses: 'both', id_token_response: 'id_token', ) end context 'when application is not set on the access token' do before do access_token.application = nil end it 'returns all default claims except audience' do expect(subject.claims).to eq( iss: 'dummy', sub: user.id.to_s, aud: nil, exp: 180, iat: 60, nonce: nonce, auth_time: 23, both_responses: 'both', id_token_response: 'id_token', ) end end end describe '#as_json' do it 'returns claims with nil values and empty strings removed' do allow(subject).to receive(:issuer).and_return(nil) allow(subject).to receive(:subject).and_return('') allow(subject).to receive(:audience).and_return(' ') json = subject.as_json expect(json).not_to include :iss expect(json).not_to include :sub expect(json).to include :aud end end describe '#as_jws_token' do shared_examples 'a jws token' do it 'returns claims encoded as JWT' do algorithms = [Doorkeeper::OpenidConnect.signing_algorithm.to_s] data, headers = ::JWT.decode subject.as_jws_token, Doorkeeper::OpenidConnect.signing_key.keypair, true, { algorithms: algorithms } expect(data.to_hash).to eq subject.as_json.stringify_keys expect(headers["kid"]).to eq Doorkeeper::OpenidConnect.signing_key.kid expect(headers["alg"]).to eq Doorkeeper::OpenidConnect.signing_algorithm.to_s end end it_behaves_like 'a jws token' context 'when signing_algorithm is EC' do before { configure_ec } it_behaves_like 'a jws token' end context 'when signing_algorithm is HMAC' do before { configure_hmac } it_behaves_like 'a jws token' end end end doorkeeper-openid_connect-1.8.7/spec/lib/id_token_token_spec.rb000066400000000000000000000017501443134665600247250ustar00rootroot00000000000000# frozen_string_literal: true require 'rails_helper' describe Doorkeeper::OpenidConnect::IdTokenToken do subject { described_class.new(access_token, nonce) } let(:access_token) { create :access_token, resource_owner_id: user.id, scopes: 'openid' } let(:user) { create :user } let(:nonce) { '123456' } before do allow(Time).to receive(:now) { Time.zone.at 60 } end describe '#claims' do it 'returns all default claims' do # access token is from http://openid.net/specs/openid-connect-core-1_0.html # so we can test `at_hash` value access_token.update(token: 'jHkWEdUXMU1BwAsC4vtUsZwnNvTIxEl0z9K3vx5KF0Y') expect(subject.claims).to eq({ iss: 'dummy', sub: user.id.to_s, aud: access_token.application.uid, exp: 180, iat: 60, nonce: nonce, auth_time: 23, at_hash: '77QmUPtjPfzWtF2AnpK9RQ', both_responses: 'both', id_token_response: 'id_token', }) end end end doorkeeper-openid_connect-1.8.7/spec/lib/oauth/000077500000000000000000000000001443134665600215075ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/lib/oauth/authorization/000077500000000000000000000000001443134665600244075ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/lib/oauth/authorization/code_spec.rb000066400000000000000000000035131443134665600266620ustar00rootroot00000000000000# frozen_string_literal: true require 'rails_helper' describe Doorkeeper::OpenidConnect::OAuth::Authorization::Code do subject { Doorkeeper::OAuth::Authorization::Code.new pre_auth, resource_owner } let(:resource_owner) { create :user } let(:access_grant) { create :access_grant } let(:pre_auth) { double } let(:client) { double } describe '#issue_token' do before do allow(pre_auth).to receive(:client) { client } allow(pre_auth).to receive(:redirect_uri).and_return('redirect_uri') allow(pre_auth).to receive(:scopes).and_return('scopes') allow(pre_auth).to receive(:nonce).and_return('123456') allow(pre_auth).to receive(:code_challenge).and_return('987654') allow(pre_auth).to receive(:code_challenge_method).and_return('plain') allow(pre_auth).to receive(:custom_access_token_attributes).and_return({}) allow(client).to receive(:id).and_return('client_id') allow(Doorkeeper::AccessGrant).to receive(:create!) { access_grant } allow(Doorkeeper::OpenidConnect::Request).to receive(:create!) end it 'stores the nonce' do subject.issue_token expect(Doorkeeper::OpenidConnect::Request).to have_received(:create!).with({ access_grant: access_grant, nonce: '123456' }) end it 'does not store the nonce if not present' do allow(pre_auth).to receive(:nonce).and_return(nil) subject.issue_token expect(Doorkeeper::OpenidConnect::Request).not_to have_received(:create!) end it 'does not store the nonce if blank' do allow(pre_auth).to receive(:nonce).and_return(' ') subject.issue_token expect(Doorkeeper::OpenidConnect::Request).not_to have_received(:create!) end it 'returns the created grant' do expect(subject.issue_token).to be_a Doorkeeper::AccessGrant end end end doorkeeper-openid_connect-1.8.7/spec/lib/oauth/authorization_code_request_spec.rb000066400000000000000000000024711443134665600305140ustar00rootroot00000000000000# frozen_string_literal: true require 'rails_helper' describe Doorkeeper::OpenidConnect::OAuth::AuthorizationCodeRequest do subject do Doorkeeper::OAuth::AuthorizationCodeRequest.new(server, grant, client).tap do |request| request.instance_variable_set '@response', response request.instance_variable_set('@access_token', token) end end let(:server) { double } let(:client) { double } let(:grant) { create :access_grant, openid_request: openid_request } let(:openid_request) { create :openid_request, nonce: '123456' } let(:token) { create :access_token } let(:response) { Doorkeeper::OAuth::TokenResponse.new token } describe '#after_successful_response' do it 'adds the ID token to the response' do subject.send :after_successful_response expect(response.id_token).to be_a Doorkeeper::OpenidConnect::IdToken expect(response.id_token.nonce).to eq '123456' end it 'destroys the OpenID request record' do grant.save! expect do subject.send :after_successful_response end.to change { Doorkeeper::OpenidConnect::Request.count }.by(-1) end it 'skips the nonce if not present' do grant.openid_request.nonce = nil subject.send :after_successful_response expect(response.id_token.nonce).to be_nil end end end doorkeeper-openid_connect-1.8.7/spec/lib/oauth/id_token_request_spec.rb000066400000000000000000000045221443134665600264150ustar00rootroot00000000000000# frozen_string_literal: true require 'rails_helper' describe Doorkeeper::OAuth::IdTokenRequest do subject do described_class.new(pre_auth, owner) end let :application do FactoryBot.create(:application, scopes: 'public') end let :pre_auth do server = Doorkeeper.configuration allow(server).to receive(:grant_flows).and_return(['implicit_oidc']) client = Doorkeeper::OAuth::Client.new(application) attributes = { client_id: client.uid, response_type: 'id_token', redirect_uri: 'https://app.com/callback', scope: 'public', nonce: '12345', } pre_auth = Doorkeeper::OAuth::PreAuthorization.new(server, attributes) pre_auth.authorizable? # triggers loading of pre_auth.client pre_auth end let(:owner) { build_stubbed(:user) } # just to make sure self created pre_auth is authorizable it 'pre_auth should be valid' do expect(pre_auth).to be_authorizable end it 'creates an access token' do expect do subject.authorize end.to change { Doorkeeper::AccessToken.count }.by(1) end it 'returns id_token response' do expect(subject.authorize).to be_a(Doorkeeper::OAuth::IdTokenResponse) end context 'token reuse' do it 'creates a new token if there are no matching tokens' do allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true) expect do subject.authorize end.to change { Doorkeeper::AccessToken.count }.by(1) end it 'creates a new token if scopes do not match' do allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true) create(:access_token, application_id: pre_auth.client.id, resource_owner_id: owner.id, scopes: '') expect do subject.authorize end.to change { Doorkeeper::AccessToken.count }.by(1) end it 'skips token creation if there is a matching one' do allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true) allow(application.scopes).to receive(:has_scopes?).and_return(true) allow(application.scopes).to receive(:all?).and_return(true) create(:access_token, application_id: pre_auth.client.id, resource_owner_id: owner.id, scopes: 'public') expect { subject.authorize }.not_to(change { Doorkeeper::AccessToken.count }) end end end doorkeeper-openid_connect-1.8.7/spec/lib/oauth/id_token_response_spec.rb000066400000000000000000000030201443134665600265530ustar00rootroot00000000000000# frozen_string_literal: true require 'rails_helper' describe Doorkeeper::OAuth::IdTokenResponse do subject { described_class.new(pre_auth, auth, id_token) } let(:token) { create :access_token } let(:application) do create(:application, scopes: 'public') end let(:pre_auth) do double( :pre_auth, client: application, redirect_uri: 'http://tst.com/cb', state: 'state', scopes: Doorkeeper::OAuth::Scopes.from_string('public'), error: nil, authorizable?: true, nonce: '12345' ) end let(:owner) { build_stubbed(:user) } let(:auth) do Doorkeeper::OAuth::Authorization::Token.new(pre_auth, owner).tap do |c| if c.respond_to?(:issue_token!) c.issue_token! else c.issue_token end end end let(:id_token) { Doorkeeper::OpenidConnect::IdToken.new(token, pre_auth) } describe '#body' do it 'return body response for id_token' do expect(subject.body).to eq({ expires_in: auth.token.expires_in_seconds, state: pre_auth.state, id_token: id_token.as_jws_token }) end end describe '#redirect_uri' do it 'includes expires_in, id_token and state' do expect(subject.redirect_uri).to include("#{pre_auth.redirect_uri}#expires_in=#{auth.token.expires_in_seconds}&" \ "state=#{pre_auth.state}&" \ "id_token=#{id_token.as_jws_token}") end it 'does not include access_token' do expect(subject.redirect_uri).not_to include('access_token') end end end doorkeeper-openid_connect-1.8.7/spec/lib/oauth/id_token_token_request_spec.rb000066400000000000000000000022551443134665600276160ustar00rootroot00000000000000# frozen_string_literal: true require 'rails_helper' describe Doorkeeper::OAuth::IdTokenTokenRequest do subject do described_class.new(pre_auth, owner) end let :application do FactoryBot.create(:application, scopes: 'public') end let :pre_auth do server = Doorkeeper.configuration allow(server).to receive(:grant_flows).and_return(['implicit_oidc']) client = Doorkeeper::OAuth::Client.new(application) attributes = { client_id: client.uid, response_type: 'id_token token', redirect_uri: 'https://app.com/callback', scope: 'public', nonce: '12345', } pre_auth = Doorkeeper::OAuth::PreAuthorization.new(server, attributes) pre_auth.authorizable? pre_auth end let(:owner) { build_stubbed(:user) } # just to make sure self created pre_auth is authorizable it 'pre_auth should be valid' do expect(pre_auth).to be_authorizable end it 'creates an access token' do expect do subject.authorize end.to change { Doorkeeper::AccessToken.count }.by(1) end it 'returns id_token token response' do expect(subject.authorize).to be_a(Doorkeeper::OAuth::IdTokenTokenResponse) end end doorkeeper-openid_connect-1.8.7/spec/lib/oauth/id_token_token_response_spec.rb000066400000000000000000000031351443134665600277620ustar00rootroot00000000000000# frozen_string_literal: true require 'rails_helper' describe Doorkeeper::OAuth::IdTokenTokenResponse do subject { described_class.new(pre_auth, auth, id_token) } let(:token) { create :access_token } let(:application) do create(:application, scopes: 'public') end let(:pre_auth) do double( :pre_auth, client: application, redirect_uri: 'http://tst.com/cb', state: 'state', scopes: Doorkeeper::OAuth::Scopes.from_string('public'), error: nil, authorizable?: true, nonce: '12345' ) end let(:owner) { build_stubbed(:user) } let(:auth) do Doorkeeper::OAuth::Authorization::Token.new(pre_auth, owner).tap do |c| if c.respond_to?(:issue_token!) c.issue_token! else c.issue_token end end end let(:id_token) { Doorkeeper::OpenidConnect::IdToken.new(token, pre_auth) } describe '#body' do it 'return body response for id_token and access_token' do expect(subject.body).to eq({ expires_in: auth.token.expires_in_seconds, state: pre_auth.state, id_token: id_token.as_jws_token, access_token: auth.token.token, token_type: auth.token.token_type }) end end describe '#redirect_uri' do it 'includes id_token, info of access_token and state' do expect(subject.redirect_uri).to include("#{pre_auth.redirect_uri}#expires_in=#{auth.token.expires_in_seconds}&" \ "state=#{pre_auth.state}&" \ "id_token=#{id_token.as_jws_token}&" \ "access_token=#{auth.token.token}&token_type=#{auth.token.token_type}") end end end doorkeeper-openid_connect-1.8.7/spec/lib/oauth/password_access_token_request_spec.rb000066400000000000000000000022731443134665600312050ustar00rootroot00000000000000# frozen_string_literal: true require 'rails_helper' describe Doorkeeper::OpenidConnect::OAuth::PasswordAccessTokenRequest do if Gem.loaded_specs['doorkeeper'].version >= Gem::Version.create('5.5.1') subject { Doorkeeper::OAuth::PasswordAccessTokenRequest.new server, client, credentials, resource_owner, { nonce: '123456' } } else subject { Doorkeeper::OAuth::PasswordAccessTokenRequest.new server, client, resource_owner, { nonce: '123456' } } end let(:server) { double } let(:client) { double } let(:credentials) { } let(:resource_owner) { create :user } let(:token) { create :access_token } let(:response) { Doorkeeper::OAuth::TokenResponse.new token } describe '#initialize' do it 'stores the nonce attribute' do expect(subject.nonce).to eq '123456' end end describe '#after_successful_response' do it 'adds the ID token to the response' do subject.instance_variable_set '@response', response subject.instance_variable_set '@access_token', token subject.send :after_successful_response expect(response.id_token).to be_a Doorkeeper::OpenidConnect::IdToken expect(response.id_token.nonce).to eq '123456' end end end doorkeeper-openid_connect-1.8.7/spec/lib/oauth/pre_authorization_spec.rb000066400000000000000000000032601443134665600266150ustar00rootroot00000000000000# frozen_string_literal: true require 'rails_helper' describe Doorkeeper::OpenidConnect::OAuth::PreAuthorization do subject { Doorkeeper::OAuth::PreAuthorization.new server, attrs } let(:server) { Doorkeeper.configuration } let(:attrs) {} describe '#initialize' do context 'with nonce parameter' do let(:attrs) { { nonce: '123456' } } it 'stores the nonce attribute' do expect(subject.nonce).to eq '123456' end end end describe '#error_response' do context 'with response_type = code' do let(:attrs) { { response_type: 'code', redirect_uri: 'client.com/callback' } } it 'redirects to redirect_uri with query parameter' do expect(subject.error_response.redirect_uri).to match(/#{attrs[:redirect_uri]}\?/) end end context 'with response_type = token' do let(:attrs) { { response_type: 'token', redirect_uri: 'client.com/callback' } } it 'redirects to redirect_uri with fragment' do expect(subject.error_response.redirect_uri).to match(/#{attrs[:redirect_uri]}#/) end end context 'with response_type = id_token' do let(:attrs) { { response_type: 'id_token', redirect_uri: 'client.com/callback' } } it 'redirects to redirect_uri with fragment' do expect(subject.error_response.redirect_uri).to match(/#{attrs[:redirect_uri]}#/) end end context 'with response_type = id_token token' do let(:attrs) { { response_type: 'id_token token', redirect_uri: 'client.com/callback' } } it 'redirects to redirect_uri with fragment' do expect(subject.error_response.redirect_uri).to match(/#{attrs[:redirect_uri]}#/) end end end end doorkeeper-openid_connect-1.8.7/spec/lib/oauth/token_response_spec.rb000066400000000000000000000026071443134665600261110ustar00rootroot00000000000000# frozen_string_literal: true require 'rails_helper' describe Doorkeeper::OpenidConnect::OAuth::TokenResponse do subject { Doorkeeper::OAuth::TokenResponse.new token } let(:token) { create :access_token } let(:client) { Doorkeeper::OAuth::Client.new create(:application) } let(:pre_auth) { Doorkeeper::OAuth::PreAuthorization.new(Doorkeeper.configuration, client_id: client.uid, nonce: '123456') } let(:id_token) { Doorkeeper::OpenidConnect::IdToken.new token, pre_auth } before do pre_auth.valid? # triggers loading of pre_auth.client end describe '#body' do before do subject.id_token = id_token end context 'with the openid scope present' do before do token.scopes = 'openid email' end it 'adds the ID token to the response' do expect(subject.body[:id_token]).to eq id_token.as_jws_token end end context 'with the openid scope present but no id_token' do before do token.scopes = 'openid email' subject.id_token = nil end it 'adds the ID token to the response' do expect(subject.body[:id_token]).to be_truthy end end context 'with the openid scope not present' do before do token.scopes = 'email' end it 'does not add the ID token to the response' do expect(subject.body).not_to include :id_token end end end end doorkeeper-openid_connect-1.8.7/spec/lib/openid_connect_spec.rb000066400000000000000000000047301443134665600247210ustar00rootroot00000000000000# frozen_string_literal: true require 'rails_helper' describe Doorkeeper::OpenidConnect do describe '.signing_algorithm' do it 'returns the signing_algorithm as an uppercase symbol' do expect(subject.signing_algorithm).to eq :RS256 end end describe '.signing_key' do it 'returns the private key as JWK instance' do expect(subject.signing_key).to be_a ::JWT::JWK::KeyBase expect(subject.signing_key.kid).to eq 'IqYwZo2cE6hsyhs48cU8QHH4GanKIx0S4Dc99kgTIMA' end end describe '.signing_key_normalized' do context 'when signing key is RSA' do it 'returns the RSA public key parameters' do expect(subject.signing_key_normalized).to eq( :kty => 'RSA', :kid => 'IqYwZo2cE6hsyhs48cU8QHH4GanKIx0S4Dc99kgTIMA', :e => 'AQAB', :n => 'sjdnSA6UWUQQHf6BLIkIEUhMRNBJC1NN_pFt1EJmEiI88GS0ceROO5B5Ooo9Y3QOWJ_n-u1uwTHBz0HCTN4wgArWd1TcqB5GQzQRP4eYnWyPfi4CfeqAHzQp-v4VwbcK0LW4FqtW5D0dtrFtI281FDxLhARzkhU2y7fuYhL8fVw5rUhE8uwvHRZ5CEZyxf7BSHxIvOZAAymhuzNLATt2DGkDInU1BmF75tEtBJAVLzWG_j4LPZh1EpSdfezqaXQlcy9PJi916UzTl0P7Yy-ulOdUsMlB6yo8qKTY1-AbZ5jzneHbGDU_O8QjYvii1WDmJ60t0jXicmOkGrOhruOptw' ) end end context 'when signing key is EC' do before { configure_ec } it 'returns the EC public key parameters' do expect(subject.signing_key_normalized).to eq( :kty => 'EC', :kid => 'dOx_AhaepicN2r2M-sxZhgkYZMCX7dYhPsNOw1ZiFnI', :crv => 'P-521', :x => 'AeYVvbl3zZcFCdE-0msqOowYODjzeXAhjsZKhdNjGlDREvko3UFOw6S43g-s8bvVBmBz3fCodEzFRYQqJVI4UFvF', :y => 'AYJ7GYeBm_Fb6liN53xGASdbRSzF34h4BDSVYzjtQc7I-1LK17fwwS3VfQCJwaT6zX33HTrhR4VoUEUJHKwR3dNs' ) end end context 'when signing key is HMAC' do before { configure_hmac } it 'returns the HMAC public key parameters' do expect(subject.signing_key_normalized).to eq( :kty => 'oct', :kid => 'lyAW7LdxryFWQtLdgxZpOrI87APHrzJKgWLT0BkWVog' ) end end end describe 'registering grant flows' do describe Doorkeeper::Request do it 'uses the correct strategy for "id_token" response types' do expect(described_class.authorization_strategy('id_token')).to eq(Doorkeeper::Request::IdToken) end it 'uses the correct strategy for "id_token token" response types' do expect(described_class.authorization_strategy('id_token token')).to eq(Doorkeeper::Request::IdTokenToken) end end end end doorkeeper-openid_connect-1.8.7/spec/lib/routes_spec.rb000066400000000000000000000021321443134665600232450ustar00rootroot00000000000000# frozen_string_literal: true require 'rails_helper' describe Doorkeeper::OpenidConnect::Rails::Routes, type: :routing do describe 'userinfo' do it 'maps GET #show' do expect(get: 'oauth/userinfo').to route_to( controller: 'doorkeeper/openid_connect/userinfo', action: 'show' ) end it 'maps POST #show' do expect(post: 'oauth/userinfo').to route_to( controller: 'doorkeeper/openid_connect/userinfo', action: 'show' ) end end describe 'discovery' do it 'maps GET #provider' do expect(get: '.well-known/openid-configuration').to route_to( controller: 'doorkeeper/openid_connect/discovery', action: 'provider' ) end it 'maps GET #webfinger' do expect(get: '.well-known/webfinger').to route_to( controller: 'doorkeeper/openid_connect/discovery', action: 'webfinger' ) end it 'maps GET #keys' do expect(get: 'oauth/discovery/keys').to route_to( controller: 'doorkeeper/openid_connect/discovery', action: 'keys' ) end end end doorkeeper-openid_connect-1.8.7/spec/lib/user_info_spec.rb000066400000000000000000000026301443134665600237200ustar00rootroot00000000000000# frozen_string_literal: true require 'rails_helper' describe Doorkeeper::OpenidConnect::UserInfo do subject { described_class.new token } let(:user) { create :user, name: 'Joe' } let(:token) { create :access_token, resource_owner_id: user.id, scopes: scopes } let(:scopes) { 'openid' } describe '#claims' do it 'returns all accessible claims' do expect(subject.claims).to eq({ sub: user.id.to_s, created_at: user.created_at.to_i, variable_name: 'openid-name', token_id: token.id, both_responses: 'both', user_info_response: 'user_info', }) end context 'with a grant for the profile scopes' do let(:scopes) { 'openid profile' } it 'returns additional profile claims' do expect(subject.claims).to eq({ sub: user.id.to_s, name: 'Joe', created_at: user.created_at.to_i, updated_at: user.updated_at.to_i, variable_name: 'profile-name', token_id: token.id, both_responses: 'both', user_info_response: 'user_info', }) end end end describe '#as_json' do it 'returns claims with nil values and empty strings removed' do allow(subject).to receive(:claims).and_return({ nil: nil, empty: '', blank: ' ', }) expect(subject.as_json).to eq({ blank: ' ' }) end end end doorkeeper-openid_connect-1.8.7/spec/models/000077500000000000000000000000001443134665600211045ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/models/access_grant_spec.rb000066400000000000000000000022441443134665600251010ustar00rootroot00000000000000# frozen_string_literal: true require 'rails_helper' describe Doorkeeper::OpenidConnect::AccessGrant do subject { Doorkeeper::AccessGrant.new } it 'has one openid_request' do association = subject.class.reflect_on_association :openid_request expect(association.options).to eq({ class_name: 'Doorkeeper::OpenidConnect::Request', inverse_of: :access_grant, foreign_key: "access_grant_id", dependent: :delete, }) end it 'extends the base doorkeeper AccessGrant' do expect(subject).to respond_to(:"openid_request=") end describe '#delete' do it 'cascades to oauth_openid_requests' do if Rails::VERSION::MAJOR >= 6 access_grant = create(:access_grant, application: create(:application)) create(:openid_request, access_grant: access_grant) expect { access_grant.delete } .to(change { Doorkeeper::OpenidConnect::Request.count }.by(-1)) else skip <<-MSG.strip Needs Rails 6 for foreign key support with sqlite3: https://blog.bigbinary.com/2019/09/24/rails-6-adds-add_foreign_key-and-remove_foreign_key-for-sqlite3.html MSG end end end end doorkeeper-openid_connect-1.8.7/spec/models/request_spec.rb000066400000000000000000000013651443134665600241400ustar00rootroot00000000000000# frozen_string_literal: true require 'rails_helper' describe Doorkeeper::OpenidConnect::Request do describe 'validations' do it 'requires an access grant' do subject.access_grant_id = nil expect(subject).not_to be_valid expect(subject.errors).to include :access_grant_id end it 'requires a nonce' do subject.nonce = nil expect(subject).not_to be_valid expect(subject.errors).to include :nonce end end describe 'associations' do it 'belongs to an access_grant' do association = subject.class.reflect_on_association :access_grant expect(association.options).to eq({ class_name: 'Doorkeeper::AccessGrant', inverse_of: :openid_request, }) end end end doorkeeper-openid_connect-1.8.7/spec/rails_helper.rb000066400000000000000000000046521443134665600226260ustar00rootroot00000000000000# frozen_string_literal: true # This file is copied to spec/ when you run 'rails generate rspec:install' ENV['RAILS_ENV'] ||= 'test' require 'dummy/config/environment' # Prevent database truncation if the environment is production abort('The Rails environment is running in production mode!') if Rails.env.production? require 'spec_helper' require 'rspec/rails' # Add additional requires below this line. Rails is not loaded until this point! # Requires supporting ruby files with custom matchers and macros, etc, in # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are # run as spec files by default. This means that files in spec/support that end # in _spec.rb will both be required and run as specs, causing the specs to be # run twice. It is recommended that you do not name files matching this glob to # end with _spec.rb. You can configure this pattern with the --pattern # option on the command line or in ~/.rspec, .rspec or `.rspec-local`. # # The following line is provided for convenience purposes. It has the downside # of increasing the boot-up time by auto-requiring all files in the support # directory. Alternatively, in the individual `*_spec.rb` files, manually # require only the support files necessary. # # Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } # Checks for pending migration and applies them before tests are run. # If you are not using ActiveRecord, you can remove this line. Dir.chdir('spec/dummy') do ActiveRecord::Migration.maintain_test_schema! end require_relative 'support/doorkeeper_configuration.rb' require 'factory_bot' FactoryBot.find_definitions RSpec.configure do |config| # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures config.fixture_path = "#{::Rails.root}/spec/fixtures" # If you're not using ActiveRecord, or you'd prefer not to run each of your # examples within a transaction, remove the following line or assign false # instead of true. config.use_transactional_fixtures = true # Filter lines from Rails gems in backtraces. config.filter_rails_from_backtrace! # arbitrary gems may also be filtered via: # config.filter_gems_from_backtrace("gem name") config.include FactoryBot::Syntax::Methods # Reinitialize configuration after each example config.after do load Rails.root.join('config/initializers/doorkeeper.rb') load Rails.root.join('config/initializers/doorkeeper_openid_connect.rb') end end doorkeeper-openid_connect-1.8.7/spec/spec_helper.rb000066400000000000000000000076261443134665600224520ustar00rootroot00000000000000# frozen_string_literal: true require 'pry-byebug' puts "====> Doorkeeper version: #{Doorkeeper::VERSION::STRING}" RSpec.configure do |config| # rspec-expectations config goes here. You can use an alternate # assertion/expectation library such as wrong or the stdlib/minitest # assertions if you prefer. config.expect_with :rspec do |expectations| # This option will default to `true` in RSpec 4. It makes the `description` # and `failure_message` of custom matchers include text for helper methods # defined using `chain`, e.g.: # be_bigger_than(2).and_smaller_than(4).description # # => "be bigger than 2 and smaller than 4" # ...rather than: # # => "be bigger than 2" expectations.include_chain_clauses_in_custom_matcher_descriptions = true end # rspec-mocks config goes here. You can use an alternate test double # library (such as bogus or mocha) by changing the `mock_with` option here. config.mock_with :rspec do |mocks| # Prevents you from mocking or stubbing a method that does not exist on # a real object. This is generally recommended, and will default to # `true` in RSpec 4. mocks.verify_partial_doubles = true end # This option will default to `:apply_to_host_groups` in RSpec 4 (and will # have no way to turn it off -- the option exists only for backwards # compatibility in RSpec 3). It causes shared context metadata to be # inherited by the metadata hash of host groups and examples, rather than # triggering implicit auto-inclusion in groups with matching metadata. config.shared_context_metadata_behavior = :apply_to_host_groups # The settings below are suggested to provide a good initial experience # with RSpec, but feel free to customize to your heart's content. # This allows you to limit a spec run to individual examples or groups # you care about by tagging them with `:focus` metadata. When nothing # is tagged with `:focus`, all examples get run. RSpec also provides # aliases for `it`, `describe`, and `context` that include `:focus` # metadata: `fit`, `fdescribe` and `fcontext`, respectively. config.filter_run_when_matching :focus # Allows RSpec to persist some state between runs in order to support # the `--only-failures` and `--next-failure` CLI options. We recommend # you configure your source control system to ignore this file. config.example_status_persistence_file_path = 'spec/examples.txt' # Limits the available syntax to the non-monkey patched syntax that is # recommended. For more details, see: # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode # config.disable_monkey_patching! # Many RSpec users commonly either run the entire suite or an individual # file, and it's useful to allow more verbose output when running an # individual spec file. if config.files_to_run.one? # Use the documentation formatter for detailed output, # unless a formatter has already been configured # (e.g. via a command-line flag). config.default_formatter = 'doc' end # Print the 10 slowest examples and example groups at the # end of the spec run, to help surface which specs are running # particularly slow. # config.profile_examples = 10 # Run specs in random order to surface order dependencies. If you find an # order dependency and want to debug it, you can fix the order by providing # the seed, which is printed after each run. # --seed 1234 config.order = :random # Seed global randomization in this process using the `--seed` CLI option. # Setting this allows you to use `--seed` to deterministically reproduce # test failures related to randomization by passing the same `--seed` value # as the one that triggered the failure. Kernel.srand config.seed end doorkeeper-openid_connect-1.8.7/spec/support/000077500000000000000000000000001443134665600213355ustar00rootroot00000000000000doorkeeper-openid_connect-1.8.7/spec/support/doorkeeper_configuration.rb000066400000000000000000000023111443134665600267450ustar00rootroot00000000000000# frozen_string_literal: true module DoorkeeperConfiguration def configure_doorkeeper(signing_key, signing_algorithm) Doorkeeper::OpenidConnect.configure do signing_key signing_key signing_algorithm signing_algorithm 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 subject do |resource_owner| resource_owner.id end end end def configure_ec signing_key = <<~EOL -----BEGIN EC PRIVATE KEY----- MIHbAgEBBEF9VcxGjPKczrJlE1N3oEpZsauQfDXIjLeini7h4/3+DOKw2VWE4lCU rNJJL65EHT+2TriRg2xSb0l0rK/MAFAFraAHBgUrgQQAI6GBiQOBhgAEAeYVvbl3 zZcFCdE+0msqOowYODjzeXAhjsZKhdNjGlDREvko3UFOw6S43g+s8bvVBmBz3fCo dEzFRYQqJVI4UFvFAYJ7GYeBm/Fb6liN53xGASdbRSzF34h4BDSVYzjtQc7I+1LK 17fwwS3VfQCJwaT6zX33HTrhR4VoUEUJHKwR3dNs -----END EC PRIVATE KEY----- EOL configure_doorkeeper(signing_key, :ES512) end def configure_hmac configure_doorkeeper('the_greatest_secret_key', :HS512) end end RSpec.configure { |config| config.include DoorkeeperConfiguration }