omniauth-practicefusion 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +28 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +131 -0
- data/Rakefile +2 -0
- data/lib/omniauth-practicefusion.rb +2 -0
- data/lib/omniauth/practicefusion.rb +2 -0
- data/lib/omniauth/practicefusion/version.rb +5 -0
- data/lib/omniauth/strategies/practicefusion.rb +127 -0
- data/omniauth-practicefusion.gemspec +28 -0
- metadata +131 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5648b53f57eb785f9d3ab77ea18dcd6d0784e6df
|
4
|
+
data.tar.gz: 0ad8b7b987221e15113a6a480905cac1934b03ab
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f3279e687df651a49fd482409053b694516527e7b65ed701fd8191012459c11eacd37c9c87a62321dd8915f00b8acdf94a7d83cbb80d4c048b856bfc033f9b88
|
7
|
+
data.tar.gz: 222877ff3c0448268b24631feed9bf8510ab9b3799af2cff558ab011b9de554586a9d9041c4040c247b389fc7ec92443d671ff613560276de1094272f915d5e2
|
data/.gitignore
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
*.rbc
|
2
|
+
.bundle
|
3
|
+
.config
|
4
|
+
.yardoc
|
5
|
+
Gemfile.lock
|
6
|
+
InstalledFiles
|
7
|
+
_yardoc
|
8
|
+
coverage
|
9
|
+
doc/
|
10
|
+
lib/bundler/man
|
11
|
+
pkg
|
12
|
+
rdoc
|
13
|
+
spec/reports
|
14
|
+
test/tmp
|
15
|
+
test/version_tmp
|
16
|
+
tmp
|
17
|
+
*.bundle
|
18
|
+
*.so
|
19
|
+
*.o
|
20
|
+
*.a
|
21
|
+
*.gem
|
22
|
+
mkmf.log
|
23
|
+
|
24
|
+
/.idea/
|
25
|
+
|
26
|
+
/.ruby-version
|
27
|
+
|
28
|
+
/omniauth-practicefusion.iml
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Sam Contapay
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
# Omniauth::PracticeFusion
|
2
|
+
|
3
|
+
OAuth 2 strategy for Practice Fusion. Redundant description :\
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
source 'http://gems.hq.practicefusion.com'
|
10
|
+
gem 'omniauth-practicefusion'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
## Usage
|
17
|
+
|
18
|
+
This works with Devise and Omniauth.
|
19
|
+
Get an App ID and Secret from Ken.
|
20
|
+
Give him your callback URL (/users/auth/practicefusion/callback).
|
21
|
+
|
22
|
+
## Configuration
|
23
|
+
|
24
|
+
```
|
25
|
+
Rails.application.config.middleware.use OmniAuth::Builder do
|
26
|
+
provider OmniAuth::Strategies::PracticeFusion, ENV['PF_OAUTH_CLIENT_ID'], ENV['PF_OAUTH_CLIENT_SECRET'], {
|
27
|
+
scope: ENV['PF_OAUTH_SCOPE'],
|
28
|
+
path_prefix: '/providers/auth',
|
29
|
+
client_options: {
|
30
|
+
site: ENV['PF_OAUTH_SITE_URL'],
|
31
|
+
}
|
32
|
+
}
|
33
|
+
end
|
34
|
+
```
|
35
|
+
|
36
|
+
## Sample Hash
|
37
|
+
|
38
|
+
```
|
39
|
+
{
|
40
|
+
"provider" => "practicefusion",
|
41
|
+
"uid" => "3cbbbd62-159d-4c40-be4c-2cdf784d6c7d",
|
42
|
+
"info" => {
|
43
|
+
"title" => "Dr",
|
44
|
+
"first_name" => "Sam",
|
45
|
+
"last_name" => "Withnoname",
|
46
|
+
"practice_guid" => "9571286c-c379-4053-9fa3-04caeb62fb69",
|
47
|
+
"email" => "sam@dev.practicefusion.com"
|
48
|
+
},
|
49
|
+
"credentials" => {
|
50
|
+
"token" => "3hvHj7PyVFH9TGyDFXaq",
|
51
|
+
"refresh_token" => "VqycQpxuBS5tjP_Kaab-",
|
52
|
+
"expires_at" => 1412279343,
|
53
|
+
"expires" => true
|
54
|
+
},
|
55
|
+
"extra" => {
|
56
|
+
"raw_info" => {
|
57
|
+
"title" => "Dr",
|
58
|
+
"firstName" => "Sam",
|
59
|
+
"lastName" => "Withnoname",
|
60
|
+
"practiceGuid" => "9571286c-c379-4053-9fa3-04caeb62fb69",
|
61
|
+
"ehrUserGuid" => "3cbbbd62-159d-4c40-be4c-2cdf784d6c7d",
|
62
|
+
"loginEmailAddress" => "sam@dev.practicefusion.com"
|
63
|
+
}
|
64
|
+
}
|
65
|
+
}
|
66
|
+
```
|
67
|
+
|
68
|
+
### Devise
|
69
|
+
|
70
|
+
First define your application id and secret in "config/initializers/devise.rb" your scope maybe different. TODO add all available scopes.
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
config.omniauth :practicefusion, ENV['PF_APP_ID'], ENV['PF_APP_SECRET'], {
|
74
|
+
scope: 'user:r_basic',
|
75
|
+
response_type: 'code'
|
76
|
+
}
|
77
|
+
```
|
78
|
+
|
79
|
+
Then add the following to 'config/routes.rb' so the callback routes are defined.
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }
|
83
|
+
```
|
84
|
+
f
|
85
|
+
Devise default callback URL is '/users/auth/practicefusion/callback' with the above route.
|
86
|
+
|
87
|
+
Make sure your model is omniauthable. Generally this is "/app/models/user.rb"
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
devise :omniauthable, :omniauth_providers => [:practicefusion]
|
91
|
+
```
|
92
|
+
|
93
|
+
Then make sure your callbacks controller is setup.
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
97
|
+
def practicefusion
|
98
|
+
@user = User.find_for_practicefusion(request.env['omniauth.auth'], current_user)
|
99
|
+
|
100
|
+
if @user.persisted?
|
101
|
+
sign_in_and_redirect @user, event: :authentication
|
102
|
+
else
|
103
|
+
redirect_to new_user_registration_url
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
```
|
108
|
+
|
109
|
+
and bind to or create the user
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
def self.find_for_practicefusion(access_token, signed_in_resource = nil)
|
113
|
+
user = User.where(ehr_user_guid: access_token['uid']).first
|
114
|
+
unless user
|
115
|
+
user = User.create(name: access_token['info']['first_name'] + ' ' + access_token['info']['last_name'], ehr_user_guid: access_token['uid'], email: access_token['info']['email'], password: Devise.friendly_token[0,20])
|
116
|
+
end
|
117
|
+
user
|
118
|
+
end
|
119
|
+
```
|
120
|
+
|
121
|
+
For your views you can login using:
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
<%= link_to "Sign in with Practice Fusion", user_omniauth_authorize_path(:practicefusion) %>
|
125
|
+
```
|
126
|
+
|
127
|
+
|
128
|
+
|
129
|
+
## Contributing
|
130
|
+
|
131
|
+
YES PLEASE LIKE SPECS
|
data/Rakefile
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'omniauth/strategies/oauth2'
|
2
|
+
require 'jwt'
|
3
|
+
|
4
|
+
module OmniAuth
|
5
|
+
module Strategies
|
6
|
+
class PracticeFusion < OmniAuth::Strategies::OAuth2
|
7
|
+
|
8
|
+
option :name, 'practicefusion'
|
9
|
+
|
10
|
+
option :authorize_options, [:response_type, :client_id, :redirect_uri, :Fauthcode, :scope]
|
11
|
+
option :client_options, {
|
12
|
+
site: 'https://api.practicefusion.com',
|
13
|
+
authorize_url: '/ehr/oauth2/auth',
|
14
|
+
token_url: '/ehr/oauth2/token'
|
15
|
+
}
|
16
|
+
|
17
|
+
uid { raw_info['ehrUserGuid'] }
|
18
|
+
|
19
|
+
info do
|
20
|
+
prune!({
|
21
|
+
title: value_or_blank(raw_info['title']),
|
22
|
+
first_name: value_or_blank(raw_info['firstName']),
|
23
|
+
last_name: value_or_blank(raw_info['lastName']),
|
24
|
+
provider_guid: value_or_blank(raw_info['providerGuid']),
|
25
|
+
practice_guid: value_or_blank(raw_info['practiceGuid']),
|
26
|
+
email: value_or_blank(raw_info['loginEmailAddress'])
|
27
|
+
})
|
28
|
+
end
|
29
|
+
|
30
|
+
extra do
|
31
|
+
hash = {}
|
32
|
+
hash[:raw_info] = raw_info
|
33
|
+
prune! hash
|
34
|
+
end
|
35
|
+
|
36
|
+
credentials do
|
37
|
+
# these are copy-paste from OmniAuth::Strategy::OAuth2
|
38
|
+
hash = {'token' => access_token.token}
|
39
|
+
hash.merge!('refresh_token' => access_token.refresh_token) if access_token.expires? && access_token.refresh_token
|
40
|
+
hash.merge!('expires_at' => access_token.expires_at) if access_token.expires?
|
41
|
+
hash.merge!('expires' => access_token.expires?)
|
42
|
+
|
43
|
+
# additional parameters returned by Practice Fusion
|
44
|
+
hash.merge!('token_type' => access_token['token_type'])
|
45
|
+
hash.merge!('scope' => access_token['scope'])
|
46
|
+
hash.merge!('resource_owner' => access_token['resource_owner'])
|
47
|
+
hash.merge!('resource_owner_authority' => access_token['resource_owner_authority'])
|
48
|
+
hash.merge!('pf_session_token' => access_token['pf_session_token']) if access_token.params.key?('pf_session_token')
|
49
|
+
hash
|
50
|
+
end
|
51
|
+
|
52
|
+
# override callback_url so we don't include query parameters
|
53
|
+
# See https://github.com/intridea/omniauth-oauth2/issues/81 for more details
|
54
|
+
def callback_url
|
55
|
+
full_host + script_name + callback_path
|
56
|
+
end
|
57
|
+
|
58
|
+
def raw_info
|
59
|
+
@raw_info ||= access_token.get('/ehr/v1/users/me').parsed
|
60
|
+
end
|
61
|
+
|
62
|
+
# add the 'authCookie' parameter to the outbound request
|
63
|
+
# the 'state' parameter becomes an encrypted JWT of the session
|
64
|
+
def authorize_params
|
65
|
+
super.merge(session['omniauth.params'].slice('authCookie')).tap do |authz_params|
|
66
|
+
# so omniauth decided (in their infinite wisdom) that instead of setting the
|
67
|
+
# omniauth.origin parameter using their session method, they were gonna access
|
68
|
+
# env['rack.session'] directly, so we need to pull that out into the session
|
69
|
+
session['omniauth.origin'] = env['rack.session']['omniauth.origin']
|
70
|
+
authz_params[:state] = JWT.encode(session, options.client_secret)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# override the callback_phase so we can validate the state token signature
|
75
|
+
def callback_phase
|
76
|
+
# If decode_state_parameter returns the correct state token, then
|
77
|
+
# request.params['state'] == session['omniauth.state']
|
78
|
+
# which passes the CSRF check of omniauth-oauth2
|
79
|
+
|
80
|
+
# If decode_state_parameter returns nil, then this is true:
|
81
|
+
# request.params['state'].to_s.empty?
|
82
|
+
# which fails the CSRF check of omniauth-oauth2
|
83
|
+
request.params['state'] = decode_state_parameter
|
84
|
+
|
85
|
+
# omniauth sets the environment up _before_ the callback_phase
|
86
|
+
# begins, but we need to set it up after we've decoded the session
|
87
|
+
@env['omniauth.origin'] = session.delete('omniauth.origin')
|
88
|
+
@env['omniauth.origin'] = nil if env['omniauth.origin'] == ''
|
89
|
+
@env['omniauth.params'] = session.delete('omniauth.params')
|
90
|
+
|
91
|
+
# And now back to our regularly scheduled program
|
92
|
+
super
|
93
|
+
end
|
94
|
+
|
95
|
+
# instead of using the session store, just collect into a hash
|
96
|
+
def session
|
97
|
+
@fake_session ||= {}
|
98
|
+
end
|
99
|
+
|
100
|
+
def session=(obj)
|
101
|
+
@fake_session = obj
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def prune!(hash)
|
107
|
+
hash.delete_if do |_, v|
|
108
|
+
prune!(v) if v.is_a?(Hash)
|
109
|
+
v.nil? || (v.respond_to?(:empty?) && v.empty?)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def value_or_blank(value)
|
114
|
+
(value.blank?) ? '' : value
|
115
|
+
end
|
116
|
+
|
117
|
+
def decode_state_parameter
|
118
|
+
# this will throw an exception if the state parameter is not valid ciphertext
|
119
|
+
# otherwise, decode the JWT and stick the object back into our fake session
|
120
|
+
self.session, _jwt = JWT.decode(request.params["state"].to_s, options.client_secret)
|
121
|
+
session['omniauth.state']
|
122
|
+
rescue JWT::VerificationError, JWT::DecodeError
|
123
|
+
nil
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'omniauth/practicefusion/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.add_dependency 'omniauth', '> 1.0'
|
8
|
+
|
9
|
+
spec.name = "omniauth-practicefusion"
|
10
|
+
spec.version = OmniAuth::PracticeFusion::VERSION
|
11
|
+
spec.authors = ["Sam Contapay, Shaun Guth"]
|
12
|
+
spec.email = ["scontapay@practicefusion.com", "sguth@practicefusion.com"]
|
13
|
+
spec.summary = %q{omniauth strategy for Practice Fusion}
|
14
|
+
spec.description = %q{Lets you use Practice Fusion as a strategy for your omniauth login flows}
|
15
|
+
spec.homepage = ""
|
16
|
+
spec.license = "MIT"
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0")
|
19
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
20
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.add_runtime_dependency 'omniauth-oauth2', '~> 1.5'
|
24
|
+
spec.add_runtime_dependency 'json-jwt', '~> 1.0', '>= 1.0.3'
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
27
|
+
spec.add_development_dependency "rake"
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: omniauth-practicefusion
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sam Contapay, Shaun Guth
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-09-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: omniauth
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: omniauth-oauth2
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.5'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.5'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: json-jwt
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.0'
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: 1.0.3
|
51
|
+
type: :runtime
|
52
|
+
prerelease: false
|
53
|
+
version_requirements: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - "~>"
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '1.0'
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 1.0.3
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: bundler
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '1.6'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '1.6'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: rake
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
description: Lets you use Practice Fusion as a strategy for your omniauth login flows
|
90
|
+
email:
|
91
|
+
- scontapay@practicefusion.com
|
92
|
+
- sguth@practicefusion.com
|
93
|
+
executables: []
|
94
|
+
extensions: []
|
95
|
+
extra_rdoc_files: []
|
96
|
+
files:
|
97
|
+
- ".gitignore"
|
98
|
+
- Gemfile
|
99
|
+
- LICENSE.txt
|
100
|
+
- README.md
|
101
|
+
- Rakefile
|
102
|
+
- lib/omniauth-practicefusion.rb
|
103
|
+
- lib/omniauth/practicefusion.rb
|
104
|
+
- lib/omniauth/practicefusion/version.rb
|
105
|
+
- lib/omniauth/strategies/practicefusion.rb
|
106
|
+
- omniauth-practicefusion.gemspec
|
107
|
+
homepage: ''
|
108
|
+
licenses:
|
109
|
+
- MIT
|
110
|
+
metadata: {}
|
111
|
+
post_install_message:
|
112
|
+
rdoc_options: []
|
113
|
+
require_paths:
|
114
|
+
- lib
|
115
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
requirements: []
|
126
|
+
rubyforge_project:
|
127
|
+
rubygems_version: 2.6.11
|
128
|
+
signing_key:
|
129
|
+
specification_version: 4
|
130
|
+
summary: omniauth strategy for Practice Fusion
|
131
|
+
test_files: []
|