omniauth-doximity-oauth2 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +134 -0
  3. data/.github/CODEOWNERS +2 -0
  4. data/.github/PULL_REQUEST_TEMPLATE.md +15 -0
  5. data/.gitignore +8 -0
  6. data/.rubocop.yml +17 -0
  7. data/CHANGELOG.md +18 -0
  8. data/CONTRIBUTING.md +31 -0
  9. data/CONTRIBUTORS.md +4 -0
  10. data/Gemfile +1 -3
  11. data/Gemfile.lock +112 -0
  12. data/LICENSE.txt +201 -0
  13. data/README.md +127 -13
  14. data/Rakefile +21 -2
  15. data/lib/omniauth/strategies/doximity_oauth2.rb +135 -0
  16. data/lib/omniauth-doximity-oauth2/errors.rb +28 -0
  17. data/lib/omniauth-doximity-oauth2/version.rb +7 -0
  18. data/lib/omniauth-doximity-oauth2.rb +4 -0
  19. data/omniauth-doximity-oauth2.gemspec +40 -0
  20. data/spec/omniauth/strategies/doximity_oauth2_spec.rb +130 -0
  21. data/spec/rubocop_spec.rb +9 -0
  22. data/spec/spec_helper.rb +4 -0
  23. data/tasks/ci.rake +16 -0
  24. data/vendor/cache/activesupport-6.1.5.1.gem +0 -0
  25. data/vendor/cache/ast-2.4.2.gem +0 -0
  26. data/vendor/cache/concurrent-ruby-1.1.10.gem +0 -0
  27. data/vendor/cache/diff-lcs-1.5.0.gem +0 -0
  28. data/vendor/cache/faraday-2.2.0.gem +0 -0
  29. data/vendor/cache/faraday-net_http-2.0.2.gem +0 -0
  30. data/vendor/cache/hashie-5.0.0.gem +0 -0
  31. data/vendor/cache/i18n-1.10.0.gem +0 -0
  32. data/vendor/cache/jwt-2.3.0.gem +0 -0
  33. data/vendor/cache/minitest-5.15.0.gem +0 -0
  34. data/vendor/cache/multi_json-1.15.0.gem +0 -0
  35. data/vendor/cache/multi_xml-0.6.0.gem +0 -0
  36. data/vendor/cache/oauth2-1.4.9.gem +0 -0
  37. data/vendor/cache/omniauth-2.1.0.gem +0 -0
  38. data/vendor/cache/omniauth-oauth2-1.7.2.gem +0 -0
  39. data/vendor/cache/openssl-3.0.0.gem +0 -0
  40. data/vendor/cache/parallel-1.22.1.gem +0 -0
  41. data/vendor/cache/parser-3.1.2.0.gem +0 -0
  42. data/vendor/cache/rack-2.2.3.gem +0 -0
  43. data/vendor/cache/rack-protection-2.2.0.gem +0 -0
  44. data/vendor/cache/rainbow-3.1.1.gem +0 -0
  45. data/vendor/cache/rake-13.0.6.gem +0 -0
  46. data/vendor/cache/rdoc-6.3.3.gem +0 -0
  47. data/vendor/cache/regexp_parser-2.3.1.gem +0 -0
  48. data/vendor/cache/rexml-3.2.5.gem +0 -0
  49. data/vendor/cache/rspec-3.11.0.gem +0 -0
  50. data/vendor/cache/rspec-core-3.11.0.gem +0 -0
  51. data/vendor/cache/rspec-expectations-3.11.0.gem +0 -0
  52. data/vendor/cache/rspec-mocks-3.11.0.gem +0 -0
  53. data/vendor/cache/rspec-support-3.11.0.gem +0 -0
  54. data/vendor/cache/rubocop-1.28.2.gem +0 -0
  55. data/vendor/cache/rubocop-ast-1.17.0.gem +0 -0
  56. data/vendor/cache/rubocop-rails-2.14.2.gem +0 -0
  57. data/vendor/cache/ruby-progressbar-1.11.0.gem +0 -0
  58. data/vendor/cache/ruby2_keywords-0.0.5.gem +0 -0
  59. data/vendor/cache/sdoc-2.3.1.gem +0 -0
  60. data/vendor/cache/tzinfo-2.0.4.gem +0 -0
  61. data/vendor/cache/unicode-display_width-2.1.0.gem +0 -0
  62. data/vendor/cache/zeitwerk-2.5.4.gem +0 -0
  63. metadata +249 -21
  64. data/lib/omniauth/doximity/oauth2/version.rb +0 -9
  65. data/lib/omniauth/doximity/oauth2.rb +0 -12
  66. data/sig/omniauth/doximity/oauth2.rbs +0 -8
data/README.md CHANGED
@@ -1,29 +1,143 @@
1
- # Omniauth::Doximity::Oauth2
1
+ # Omniauth::DoximityOauth2
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/omniauth/doximity/oauth2`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ OmniAuth strategy for Doximity.
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
5
+ Sign up for Doximity's API to get your OAuth credentials at: https://www.doximity.com/developers/api_signup
6
6
 
7
- ## Installation
7
+ For more details on what tools we have available, read our developer docs: https://www.doximity.com/developers/documentation
8
8
 
9
- Install the gem and add to the application's Gemfile by executing:
9
+ ## Installation
10
10
 
11
- $ bundle add omniauth-doximity-oauth2
11
+ Add to your `Gemfile`:
12
12
 
13
- If bundler is not being used to manage dependencies, install the gem by executing:
13
+ ```ruby
14
+ gem 'omniauth-doximity-oauth2'
15
+ ```
14
16
 
15
- $ gem install omniauth-doximity-oauth2
17
+ Then `bundle install`.
16
18
 
17
19
  ## Usage
18
20
 
19
- TODO: Write usage instructions here
21
+ Here's an example for adding the middleware to a Rails app in `config/initializers/omniauth.rb`:
22
+
23
+ ```ruby
24
+ DOXIMITY_OMNIAUTH_SETUP = lambda do |env|
25
+ env['omniauth.strategy'].options[:client_id] = ENV["DOXIMITY_CLIENT_ID"]
26
+ env['omniauth.strategy'].options[:client_secret] = ENV["DOXIMITY_CLIENT_SECRET"]
27
+ env['omniauth.strategy'].options[:scope] = "openid profile:read:basic profile:read:email"
28
+ end
29
+
30
+ Rails.application.config.middleware.use OmniAuth::Builder do
31
+ configure do |config|
32
+ config.path_prefix = '/auth'
33
+ end
34
+ provider :doximity_oauth2, setup: DOXIMITY_OMNIAUTH_SETUP
35
+ end
36
+ ```
37
+
38
+ Talk with the Doximity API team about what scopes you need for your application, and make sure to edit your OmniAuth initializer to request them.
39
+
40
+ Update your `config/routes.rb` to support Doximity OmniAuth callbacks on your session controller:
41
+
42
+ ```ruby
43
+ Rails.application.routes.draw do
44
+ get "/auth/:provider/callback" => "sessions#create"
45
+ post "/signout" => "sessions#destroy"
46
+ get "/auth/failure" => "sessions#failure"
47
+ end
48
+ ```
49
+
50
+ Then, create a sign-in button that posts to `/auth/doximity`. Use one of the Sign in with Doximity logos, available here: https://www.doximity.com/developers/documentation#logos-for-use-by-third-party-developers
51
+
52
+ ```ruby
53
+ <%= link_to "Sign in with Doximity", "/auth/doximity", method: :post do %>
54
+ <%= image_tag "https://assets.doxcdn.com/image/upload/v1/apps/doximity/api/api-button-sign-in-with-doximity.png", alt: "Sign in with Doximity button"%>
55
+ <% end %>
56
+ ```
57
+
58
+ Note that in OmniAuth versions 2 and above, links to sign in should use the POST method. Read more [here](https://github.com/omniauth/omniauth/wiki/Resolving-CVE-2015-9284)
59
+
60
+ In your callback controller, you will have a few resources available to you after the user approves your application and logs in.
61
+
62
+ ```ruby
63
+ class SessionsController < ApplicationController
64
+ def create
65
+ session[:user_uuid] = request.env["omniauth.auth"]["uid"]
66
+ redirect_to request.env["omniauth.origin"] || "/", :notice => "Signed in!"
67
+ end
20
68
 
21
- ## Development
69
+ def destroy
70
+ session.delete(:user_uuid)
71
+ redirect_to "/"
72
+ end
22
73
 
23
- After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
74
+ def failure
75
+ redirect_to request.env["omniauth.origin"] || "/", :alert => "Authentication error: #{params[:message].humanize}"
76
+ end
77
+ end
78
+ ```
24
79
 
25
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
80
+ You can also add an `origin` param to your `/auth/doximity` post, which will be provided in the `request.env["omniauth.origin"]` variable after the success or failure callback.
81
+
82
+ ## Configuration
83
+
84
+ You can configure several options, inside the configuration lambda:
85
+
86
+ * `[:scope]`: A comma-separated list of permissions you want to request from the user.Caveats:
87
+ * The `openid` scope is suggested. Alternatively, if the `openid` scope is not requested `omniauth-doximity` will make an additional request to retrieve information about the signed in user using your other scopes. Your app may be subject to rate limiting depending on your usage.
88
+ * Without any scopes, you will still be able to log in the user and retrieve a unique UUIDv4 to distinguish them from other users.
89
+
90
+ * `[:name]`: The name of the strategy. The default name is `doximity` but it can be changed to any string. The `:provider` part of OmniAuth URLs will also change to `/auth/{{ name }}`.
91
+
92
+ * `[:client_options][:site]`: Override the Doximity OAuth provider website. You may be provided with a development site to use while setting up your integration, which you would set here.
93
+
94
+ * `[:pkce]`: A boolean denoting whether to follow the PKCE OAuth spec. Default `true`. Note that if set to false, your OmniAuth credentials hash will not include a `refresh` token. Your OAuth application also may require PKCE to use OmniAuth.
95
+
96
+ ## Auth Hash
97
+
98
+ Here's an example of an authentication hash available in the callback by accessing `request.env['omniauth.auth']`:
99
+
100
+ ```ruby
101
+ {
102
+ "provider" => "doximity",
103
+ "uid" => "cc485bd2-b25a-4677-b05c-e98febf7789d",
104
+ "info" => {
105
+ "name" => "Test User",
106
+ "given_name" => "Test",
107
+ "family_name" => "User",
108
+ "primary_email" => "md@doximity.com",
109
+ "emails" => ["md@doximity.com"],
110
+ "profile_photo_url" => "http://res.cloudinary.com/doximity-development/image/upload/l_text:Helvetica_130_bold:AT,co_rgb:FFFFFF,t_profile_photo_320x320/profile-placeholder-registered-5.jpg",
111
+ "credentials" => "Other",
112
+ "specialty" => "Optometrist"
113
+ },
114
+ "credentials" => {
115
+ "token" => "gMej-ecC9Wzy4KkUCypYQ1J_8mQ1Yo9RXJYwU2kCyPKciuuOIxHflFlLP0PLlJmwnjPwlNa7nkQeeOcz-zyC6w==",
116
+ "refresh_token" => "go-40T6xPOzSOd09NTElQ0tGi-BU5hluljET8wa3syzxBqsG5BP0PJW_CsbDhmm49T081jhsIMnP-OQG8McYYPdOENc027K87gGSurOquANzx8qlo4hTJ903LNGpTZ6VcV1Ci0jomvJdH1NsCq5nLxeCy4dBctTZEMA-c3pOVZ0=",
117
+ "expires_at" => 1650335410,
118
+ "expires" => true,
119
+ "access_token" => "gMej-ecC9Wzy4KkUCypYQ1J_8mQ1Yo9RXJYwU2kCyPKciuuOIxHflFlLP0PLlJmwnjPwlNa7nkQeeOcz-zyC6w==",
120
+ "scope" => "profile:read:email profile:read:basic openid",
121
+ "token_type" => "bearer"
122
+ }, "extra" => {
123
+ "raw_subject_info" => {
124
+ "acr" => 2, "at_hash" => "uVfpy56HzI3J_dZR2kyxrQ", "aud" => ["https://auth.doximity.com", "6bd7e37e80fd06819ca13b268adea5fbe57446a9f9e1982f9483813d7272acf1"], "auth_time" => 1650333376, "azp" => "6bd7e37e80fd06819ca13b268adea5fbe57446a9f9e1982f9483813d7272acf1", "credentials" => "Other", "emails" => ["md@doximity.com"], "exp" => 1650335384, "family_name" => "User", "given_name" => "Test", "iat" => 1650333610, "iss" => "https://auth.doximity.com", "name" => "Test User", "primary_email" => "md@doximity.com", "profile_photo_url" => "http://res.cloudinary.com/doximity-development/image/upload/l_text:Helvetica_130_bold:AT,co_rgb:FFFFFF,t_profile_photo_320x320/profile-placeholder-registered-5.jpg", "sid" => "9", "specialty" => "Optometrist", "sub" => "cc485bd2-b25a-4677-b05c-e98febf7789d"
125
+ }, "raw_credential_info" => {
126
+ "token_type" => "bearer", "scope" => "profile:read:email profile:read:basic openid", "id_token" => "{{JWT omitted for brevity}}", "access_token" => "gMej-ecC9Wzy4KkUCypYQ1J_8mQ1Yo9RXJYwU2kCyPKciuuOIxHflFlLP0PLlJmwnjPwlNa7nkQeeOcz-zyC6w==", "refresh_token" => "go-40T6xPOzSOd09NTElQ0tGi-BU5hluljET8wa3syzxBqsG5BP0PJW_CsbDhmm49T081jhsIMnP-OQG8McYYPdOENc027K87gGSurOquANzx8qlo4hTJ903LNGpTZ6VcV1Ci0jomvJdH1NsCq5nLxeCy4dBctTZEMA-c3pOVZ0=", "expires_at" => 1650335410
127
+ }
128
+ }
129
+ }
130
+ ```
26
131
 
27
132
  ## Contributing
28
133
 
29
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/omniauth-doximity-oauth2.
134
+ 1. Fork it
135
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
136
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
137
+ 4. Push to the branch (`git push origin my-new-feature`)
138
+ 5. Create a new Pull Request
139
+ 6. Sign the CLA if you haven't yet. See [CONTRIBUTING.md](./CONTRIBUTING.md)
140
+
141
+ ## License
142
+
143
+ The gem is licensed under an Apache 2 license. Contributors are required to sign a contributor license agreement. See [LICENSE.txt](./LICENSE.txt) and [CONTRIBUTING.md](./CONTRIBUTING.md) for more information.
data/Rakefile CHANGED
@@ -1,4 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "bundler/gem_tasks"
4
- task default: %i[]
3
+ require File.join("bundler", "gem_tasks")
4
+ require File.join("rspec", "core", "rake_task")
5
+ require "sdoc"
6
+
7
+ FileList["tasks/*.rake"].each { |task| load task }
8
+
9
+ RSpec::Core::RakeTask.new(:spec)
10
+
11
+ task default: :spec
12
+
13
+ RDoc::Task.new do |rdoc|
14
+ rdoc.main = "README.md"
15
+ rdoc.markup = "tomdoc"
16
+ rdoc.options << "--format=sdoc"
17
+ rdoc.options << "--github --encoding=UTF-8"
18
+ rdoc.rdoc_dir = "doc"
19
+ rdoc.rdoc_files.exclude("vendor", "tmp")
20
+ rdoc.rdoc_files.include("README.md", "lib", "*.rb")
21
+ rdoc.template = "rails"
22
+ rdoc.title = "omniauth-doximity Documentation"
23
+ end
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "omniauth/strategies/oauth2"
4
+ require "omniauth-doximity-oauth2/errors"
5
+ require "active_support/core_ext/hash/indifferent_access"
6
+ require "uri"
7
+ require "rack/utils"
8
+ require "jwt"
9
+ require "faraday"
10
+ require "multi_json"
11
+
12
+ module OmniAuth
13
+ module Strategies
14
+ # Doximity OmniAuth strategy.
15
+ class DoximityOauth2 < OmniAuth::Strategies::OAuth2
16
+ DEFAULT_SCOPE = "openid profile:read:basic"
17
+
18
+ option :name, "doximity"
19
+
20
+ option :pkce, true
21
+
22
+ option :authorize_options, [:scope]
23
+
24
+ option :client_options, {
25
+ site: "https://auth.doximity.com",
26
+ authorize_url: "/oauth/authorize",
27
+ token_url: "/oauth/token",
28
+ jwks_url: "/.well-known/jwks.json"
29
+ }
30
+
31
+ option :auth_token_params, {
32
+ mode: :header
33
+ }
34
+
35
+ uid { raw_subject_info["sub"] }
36
+
37
+ info do
38
+ prune({
39
+ name: raw_subject_info["name"],
40
+ given_name: raw_subject_info["given_name"],
41
+ middle_name: raw_subject_info["middle_name"],
42
+ family_name: raw_subject_info["family_name"],
43
+ primary_email: raw_subject_info["primary_email"],
44
+ emails: raw_subject_info["emails"],
45
+ profile_photo_url: raw_subject_info["profile_photo_url"],
46
+ credentials: raw_subject_info["credentials"],
47
+ specialty: raw_subject_info["specialty"],
48
+ permissions: raw_subject_info["permissions"]
49
+ })
50
+ end
51
+
52
+ extra do
53
+ prune({
54
+ raw_subject_info: raw_subject_info,
55
+ raw_credential_info: raw_credential_info
56
+ })
57
+ end
58
+
59
+ credentials do
60
+ prune({
61
+ access_token: raw_credential_info["access_token"],
62
+ refresh_token: raw_credential_info["refresh_token"],
63
+ expires_at: raw_credential_info["expires_at"],
64
+ scope: raw_credential_info["scope"],
65
+ token_type: raw_credential_info["token_type"]
66
+ })
67
+ end
68
+
69
+ def raw_subject_info
70
+ @raw_subject_info ||= parse_id_token(access_token["id_token"] || access_token.get("/oauth/userinfo").body) || {}
71
+ end
72
+
73
+ def raw_credential_info
74
+ @raw_credential_info ||= access_token.to_hash.with_indifferent_access
75
+ end
76
+
77
+ def authorize_params
78
+ super.tap do |params|
79
+ options[:authorize_options].each do |v|
80
+ params[v.to_sym] = request.params[v] if request.params[v]
81
+ end
82
+
83
+ params[:scope] = get_scope(params)
84
+ end
85
+ end
86
+
87
+ private
88
+
89
+ def get_scope(params)
90
+ raw_scope = params[:scope] || DEFAULT_SCOPE
91
+ scope_list = raw_scope.split(" ").map { |item| item.split(",") }.flatten
92
+ scope_list.join(" ")
93
+ end
94
+
95
+ def parse_id_token(token)
96
+ _, header = JWT.decode(token, nil, false)
97
+
98
+ keys = request_keys
99
+
100
+ public_key_params = keys.find { |key| key["kid"] == header["kid"] }
101
+ rsa_key = create_rsa_key(public_key_params["n"], public_key_params["e"])
102
+
103
+ body, = JWT.decode(token, rsa_key.public_key, true, { algorithm: header["alg"] })
104
+ body
105
+ rescue JWT::VerificationError => e
106
+ raise OmniAuth::DoximityOauth2::JWTVerificationError(e, token)
107
+ end
108
+
109
+ def callback_url
110
+ options[:callback_url] || full_host + script_name + callback_path
111
+ end
112
+
113
+ def prune(hash)
114
+ hash.delete_if do |_, val|
115
+ prune(val) if val.is_a?(Hash)
116
+ val.nil? || (val.respond_to?(:empty?) && val.empty?)
117
+ end
118
+ end
119
+
120
+ def request_keys
121
+ url = options[:client_options][:site] + options[:client_options][:jwks_url]
122
+ response = Faraday.get(url)
123
+
124
+ raise OmniAuth::DoximityOauth2::JWKSRequestError(url, response) if response.status != 200
125
+
126
+ MultiJson.load(response.body)["keys"]
127
+ end
128
+
129
+ def create_rsa_key(n, e)
130
+ key = OpenSSL::PKey::RSA.new
131
+ key.set_key(OpenSSL::BN.new(Base64.urlsafe_decode64(n), 2), OpenSSL::BN.new(Base64.urlsafe_decode64(e), 2), nil)
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Omniauth
4
+ module DoximityOauth2
5
+ # Error for failed request to get public keys, for JWK verification
6
+ class JWKSRequestError < StandardError
7
+ MESSAGE = "Failed to request public keys for user info verification"
8
+ attr_reader :url, :response
9
+
10
+ def initialize(url, response)
11
+ @url = url
12
+ @response = response
13
+ super(MESSAGE)
14
+ end
15
+ end
16
+
17
+ # Error for failed JWK verifications
18
+ class JWTVerificationError < StandardError
19
+ MESSAGE = "Failed to verify user info JWT"
20
+ attr_reader :token, :error
21
+
22
+ def initialize(error, token)
23
+ @token = token
24
+ super(error.message)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Omniauth
4
+ module DoximityOauth2
5
+ VERSION = "1.0.0"
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "omniauth-doximity-oauth2/version"
4
+ require "omniauth/strategies/doximity_oauth2"
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'English'
4
+ require File.expand_path('lib/omniauth-doximity-oauth2/version', __dir__)
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "omniauth-doximity-oauth2"
8
+ spec.version = Omniauth::DoximityOauth2::VERSION
9
+ spec.authors = ["William Harvey"]
10
+ spec.email = ["wharvey@doximity.com"]
11
+ spec.description = 'OmniAuth strategy for Doximity, supporting OIDC, and using PKCE'
12
+ spec.summary = 'OmniAuth strategy for Doximity'
13
+ spec.homepage = "https://github.com/doximity/omniauth-doximity-oauth2.git"
14
+ spec.license = "Apache-2.0"
15
+
16
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
17
+
18
+ spec.metadata["homepage_uri"] = spec.homepage
19
+ spec.metadata["source_code_uri"] = spec.homepage
20
+ spec.metadata["changelog_uri"] = "https://github.com/doximity/omniauth-doximity-oauth2/blob/master/CHANGELOG.md"
21
+
22
+ spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
23
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
24
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
25
+ spec.require_paths = ["lib"]
26
+
27
+ spec.add_runtime_dependency "activesupport"
28
+ spec.add_runtime_dependency "faraday"
29
+ spec.add_runtime_dependency "jwt"
30
+ spec.add_runtime_dependency "multi_json"
31
+ spec.add_runtime_dependency "omniauth-oauth2"
32
+ spec.add_runtime_dependency "openssl"
33
+
34
+ spec.add_development_dependency "bundler", "~> 2.3.12"
35
+ spec.add_development_dependency "rake"
36
+ spec.add_development_dependency "rspec"
37
+ spec.add_development_dependency "rubocop"
38
+ spec.add_development_dependency "rubocop-rails"
39
+ spec.add_development_dependency "sdoc"
40
+ end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+ require "json"
5
+ require "omniauth-doximity-oauth2"
6
+ require "stringio"
7
+
8
+ describe OmniAuth::Strategies::DoximityOauth2 do
9
+ let(:request) { double("Request", params: {}, cookies: {}, env: {}) }
10
+ let(:app) do
11
+ lambda do
12
+ [200, {}, ["Hello."]]
13
+ end
14
+ end
15
+
16
+ subject do
17
+ OmniAuth::Strategies::DoximityOauth2.new(app, "appid", "secret", @options || {}).tap do |strategy|
18
+ allow(strategy).to receive(:request) do
19
+ request
20
+ end
21
+ end
22
+ end
23
+
24
+ before do
25
+ OmniAuth.config.test_mode = true
26
+ end
27
+
28
+ after do
29
+ OmniAuth.config.test_mode = false
30
+ end
31
+
32
+ describe "#client_options" do
33
+ it "has correct site" do
34
+ expect(subject.client.site).to eq("https://auth.doximity.com")
35
+ end
36
+
37
+ it "has correct authorize_url" do
38
+ expect(subject.client.options[:authorize_url]).to eq("/oauth/authorize")
39
+ end
40
+
41
+ it "has correct token_url" do
42
+ expect(subject.client.options[:token_url]).to eq("/oauth/token")
43
+ end
44
+
45
+ it "has correct jwks_url" do
46
+ expect(subject.client.options[:jwks_url]).to eq("/.well-known/jwks.json")
47
+ end
48
+
49
+ describe "overrides" do
50
+ context "as strings" do
51
+ it "should allow overriding the site" do
52
+ @options = { client_options: { "site" => "https://example.com" } }
53
+ expect(subject.client.site).to eq("https://example.com")
54
+ end
55
+
56
+ it "should allow overriding the authorize_url" do
57
+ @options = { client_options: { "authorize_url" => "/example" } }
58
+ expect(subject.client.options[:authorize_url]).to eq("/example")
59
+ end
60
+
61
+ it "should allow overriding the token_url" do
62
+ @options = { client_options: { "token_url" => "/example" } }
63
+ expect(subject.client.options[:token_url]).to eq("/example")
64
+ end
65
+
66
+ it "should allow overriding the jwks_url" do
67
+ @options = { client_options: { "jwks_url" => "/example" } }
68
+ expect(subject.client.options[:jwks_url]).to eq("/example")
69
+ end
70
+ end
71
+
72
+ context "as symbols" do
73
+ it "should allow overriding the site" do
74
+ @options = { client_options: { site: "https://example.com" } }
75
+ expect(subject.client.site).to eq("https://example.com")
76
+ end
77
+
78
+ it "should allow overriding the authorize_url" do
79
+ @options = { client_options: { authorize_url: "/example" } }
80
+ expect(subject.client.options[:authorize_url]).to eq("/example")
81
+ end
82
+
83
+ it "should allow overriding the token_url" do
84
+ @options = { client_options: { token_url: "/example" } }
85
+ expect(subject.client.options[:token_url]).to eq("/example")
86
+ end
87
+
88
+ it "should allow overriding the jwks_url" do
89
+ @options = { client_options: { jwks_url: "/example" } }
90
+ expect(subject.client.options[:jwks_url]).to eq("/example")
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ describe "#authorize_options" do
97
+ %i[scope].each do |k|
98
+ it "should support #{k}" do
99
+ @options = { k => "http://someval" }
100
+ expect(subject.authorize_params[k.to_s]).to eq("http://someval")
101
+ end
102
+ end
103
+
104
+ describe "scope" do
105
+ it "should leave base scopes as is" do
106
+ @options = { scope: "profile:read:basic" }
107
+ expect(subject.authorize_params["scope"]).to eq("profile:read:basic")
108
+ end
109
+
110
+ it "should join scopes" do
111
+ @options = { scope: "profile:read:basic,profile:read:email" }
112
+ expect(subject.authorize_params["scope"]).to eq("profile:read:basic profile:read:email")
113
+ end
114
+
115
+ it "should deal with whitespace when joining scopes" do
116
+ @options = { scope: "profile:read:basic, profile:read:email" }
117
+ expect(subject.authorize_params["scope"]).to eq("profile:read:basic profile:read:email")
118
+ end
119
+
120
+ it "should set default scope to openid profile:read:basic" do
121
+ expect(subject.authorize_params["scope"]).to eq("openid profile:read:basic")
122
+ end
123
+
124
+ it "should support space delimited scopes" do
125
+ @options = { scope: "profile:read:basic profile:read:email" }
126
+ expect(subject.authorize_params["scope"]).to eq("profile:read:basic profile:read:email")
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "spec_helper"
4
+
5
+ describe "Rubocop" do
6
+ it "should pass with no offenses detected" do
7
+ expect(`rubocop`).to include("no offenses detected")
8
+ end
9
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.join("bundler", "setup")
4
+ require "rspec"
data/tasks/ci.rake ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :ci do
4
+ desc "Run tests"
5
+ task :specs do
6
+ sh "bundle exec rspec --color spec --format progress"
7
+ end
8
+
9
+ desc "Run rubocop"
10
+ task :rubocop do
11
+ sh "bundle exec rubocop --display-cop-names --extra-details --display-style-guide"
12
+ end
13
+
14
+ desc "Build documentation"
15
+ task doc: :rdoc
16
+ end
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file