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.
- checksums.yaml +4 -4
- data/.circleci/config.yml +134 -0
- data/.github/CODEOWNERS +2 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +15 -0
- data/.gitignore +8 -0
- data/.rubocop.yml +17 -0
- data/CHANGELOG.md +18 -0
- data/CONTRIBUTING.md +31 -0
- data/CONTRIBUTORS.md +4 -0
- data/Gemfile +1 -3
- data/Gemfile.lock +112 -0
- data/LICENSE.txt +201 -0
- data/README.md +127 -13
- data/Rakefile +21 -2
- data/lib/omniauth/strategies/doximity_oauth2.rb +135 -0
- data/lib/omniauth-doximity-oauth2/errors.rb +28 -0
- data/lib/omniauth-doximity-oauth2/version.rb +7 -0
- data/lib/omniauth-doximity-oauth2.rb +4 -0
- data/omniauth-doximity-oauth2.gemspec +40 -0
- data/spec/omniauth/strategies/doximity_oauth2_spec.rb +130 -0
- data/spec/rubocop_spec.rb +9 -0
- data/spec/spec_helper.rb +4 -0
- data/tasks/ci.rake +16 -0
- data/vendor/cache/activesupport-6.1.5.1.gem +0 -0
- data/vendor/cache/ast-2.4.2.gem +0 -0
- data/vendor/cache/concurrent-ruby-1.1.10.gem +0 -0
- data/vendor/cache/diff-lcs-1.5.0.gem +0 -0
- data/vendor/cache/faraday-2.2.0.gem +0 -0
- data/vendor/cache/faraday-net_http-2.0.2.gem +0 -0
- data/vendor/cache/hashie-5.0.0.gem +0 -0
- data/vendor/cache/i18n-1.10.0.gem +0 -0
- data/vendor/cache/jwt-2.3.0.gem +0 -0
- data/vendor/cache/minitest-5.15.0.gem +0 -0
- data/vendor/cache/multi_json-1.15.0.gem +0 -0
- data/vendor/cache/multi_xml-0.6.0.gem +0 -0
- data/vendor/cache/oauth2-1.4.9.gem +0 -0
- data/vendor/cache/omniauth-2.1.0.gem +0 -0
- data/vendor/cache/omniauth-oauth2-1.7.2.gem +0 -0
- data/vendor/cache/openssl-3.0.0.gem +0 -0
- data/vendor/cache/parallel-1.22.1.gem +0 -0
- data/vendor/cache/parser-3.1.2.0.gem +0 -0
- data/vendor/cache/rack-2.2.3.gem +0 -0
- data/vendor/cache/rack-protection-2.2.0.gem +0 -0
- data/vendor/cache/rainbow-3.1.1.gem +0 -0
- data/vendor/cache/rake-13.0.6.gem +0 -0
- data/vendor/cache/rdoc-6.3.3.gem +0 -0
- data/vendor/cache/regexp_parser-2.3.1.gem +0 -0
- data/vendor/cache/rexml-3.2.5.gem +0 -0
- data/vendor/cache/rspec-3.11.0.gem +0 -0
- data/vendor/cache/rspec-core-3.11.0.gem +0 -0
- data/vendor/cache/rspec-expectations-3.11.0.gem +0 -0
- data/vendor/cache/rspec-mocks-3.11.0.gem +0 -0
- data/vendor/cache/rspec-support-3.11.0.gem +0 -0
- data/vendor/cache/rubocop-1.28.2.gem +0 -0
- data/vendor/cache/rubocop-ast-1.17.0.gem +0 -0
- data/vendor/cache/rubocop-rails-2.14.2.gem +0 -0
- data/vendor/cache/ruby-progressbar-1.11.0.gem +0 -0
- data/vendor/cache/ruby2_keywords-0.0.5.gem +0 -0
- data/vendor/cache/sdoc-2.3.1.gem +0 -0
- data/vendor/cache/tzinfo-2.0.4.gem +0 -0
- data/vendor/cache/unicode-display_width-2.1.0.gem +0 -0
- data/vendor/cache/zeitwerk-2.5.4.gem +0 -0
- metadata +249 -21
- data/lib/omniauth/doximity/oauth2/version.rb +0 -9
- data/lib/omniauth/doximity/oauth2.rb +0 -12
- data/sig/omniauth/doximity/oauth2.rbs +0 -8
data/README.md
CHANGED
@@ -1,29 +1,143 @@
|
|
1
|
-
# Omniauth::
|
1
|
+
# Omniauth::DoximityOauth2
|
2
2
|
|
3
|
-
|
3
|
+
OmniAuth strategy for Doximity.
|
4
4
|
|
5
|
-
|
5
|
+
Sign up for Doximity's API to get your OAuth credentials at: https://www.doximity.com/developers/api_signup
|
6
6
|
|
7
|
-
|
7
|
+
For more details on what tools we have available, read our developer docs: https://www.doximity.com/developers/documentation
|
8
8
|
|
9
|
-
|
9
|
+
## Installation
|
10
10
|
|
11
|
-
|
11
|
+
Add to your `Gemfile`:
|
12
12
|
|
13
|
-
|
13
|
+
```ruby
|
14
|
+
gem 'omniauth-doximity-oauth2'
|
15
|
+
```
|
14
16
|
|
15
|
-
|
17
|
+
Then `bundle install`.
|
16
18
|
|
17
19
|
## Usage
|
18
20
|
|
19
|
-
|
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
|
-
|
69
|
+
def destroy
|
70
|
+
session.delete(:user_uuid)
|
71
|
+
redirect_to "/"
|
72
|
+
end
|
22
73
|
|
23
|
-
|
74
|
+
def failure
|
75
|
+
redirect_to request.env["omniauth.origin"] || "/", :alert => "Authentication error: #{params[:message].humanize}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
```
|
24
79
|
|
25
|
-
|
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
|
-
|
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
|
4
|
-
|
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,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
|
data/spec/spec_helper.rb
ADDED
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
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|