Skip to content

Commit e358a6d

Browse files
joshbukerCh4s3
authored andcommitted
Add Microsoft Graph Provider (#37)
* Add provider for Microsoft Graph based on existing google provider * Increment version to 0.10.2 * Add graph provider require to external module * Add graph specs based on wechat specs * Add byebug as a development dependency * Fix default scope and params for Microsoft Graph * Rename Microsoft Graph provider from graph to microsoft * Update generator to match default * Update CHANGELOG.md
1 parent b82f72d commit e358a6d

File tree

11 files changed

+120
-38
lines changed

11 files changed

+120
-38
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ doc
1414
# jeweler generated
1515
pkg
1616

17+
# byebug generated
18+
.byebug_history
19+
1720
# for RVM
1821
.rvmrc
1922

CHANGELOG.md

Lines changed: 9 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
2+
3+
## 0.10.2
4+
5+
* Added support for Microsoft OAuth (thanks to @athix) [#37](https://github.com/Sorcery/sorcery/pull/37)
6+
27
## 0.10.1
8+
39
* Fixed LinkedIn bug [#36](https://github.com/Sorcery/sorcery/pull/36)
410

511
## 0.10.0
@@ -77,46 +83,17 @@
7783

7884
## 0.8.4
7985

80-
* Few security fixes in `external` module
86+
* Few security fixes in `external` module
8187

8288
## 0.8.3 (yanked because of bad Jeweler release)
8389

8490
## 0.8.2
8591

8692
* Activity logging feature has a new column called `last_login_from_ip_address` (string type). If you use ActiveRecord, you will have to add this column to DB ([#465](https://github.com/NoamB/sorcery/issues/465))
8793

88-
## 0.8.1
89-
<!-- TO BE WRITTEN -->
90-
91-
## 0.8.0
92-
<!-- TO BE WRITTEN -->
93-
94-
## 0.7.13
95-
<!-- TO BE WRITTEN -->
96-
97-
## 0.7.12
98-
<!-- TO BE WRITTEN -->
99-
100-
## 0.7.11
101-
<!-- TO BE WRITTEN -->
102-
103-
## 0.7.10
104-
<!-- TO BE WRITTEN -->
105-
106-
## 0.7.9
107-
<!-- TO BE WRITTEN -->
108-
109-
## 0.7.8
110-
<!-- TO BE WRITTEN -->
111-
112-
## 0.7.7
113-
<!-- TO BE WRITTEN -->
114-
115-
## 0.7.6
116-
<!-- TO BE WRITTEN -->
94+
## 0.7.5-0.8.1
11795

118-
## 0.7.5
119-
<!-- TO BE WRITTEN -->
96+
<!-- HERE BE DRAGONS (Changelogs never written) -->
12097

12198
## 0.7.1-0.7.4
12299

lib/generators/sorcery/templates/initializer.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,16 @@
131131
# config.google.user_info_mapping = {:email => "email", :username => "name"}
132132
# config.google.scope = "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile"
133133
#
134+
# For Microsoft Graph, the key will be your App ID, and the secret will be your app password/public key.
135+
# The callback URL "can't contain a query string or invalid special characters", see: https://docs.microsoft.com/en-us/azure/active-directory/active-directory-v2-limitations#restrictions-on-redirect-uris
136+
# More information at https://graph.microsoft.io/en-us/docs
137+
#
138+
# config.microsoft.key = ""
139+
# config.microsoft.secret = ""
140+
# config.microsoft.callback_url = "http://0.0.0.0:3000/oauth/callback/microsoft"
141+
# config.microsoft.user_info_mapping = {:email => "userPrincipalName", :username => "displayName"}
142+
# config.microsoft.scope = "openid email https://graph.microsoft.com/User.Read"
143+
#
134144
# config.vk.key = ""
135145
# config.vk.secret = ""
136146
# config.vk.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=vk"

lib/sorcery/controller/submodules/external.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ def self.included(base)
2222
require 'sorcery/providers/paypal'
2323
require 'sorcery/providers/slack'
2424
require 'sorcery/providers/wechat'
25+
require 'sorcery/providers/microsoft'
2526

2627
Config.module_eval do
2728
class << self

lib/sorcery/providers/microsoft.rb

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
module Sorcery
2+
module Providers
3+
# This class adds support for OAuth with Microsoft Graph.
4+
#
5+
# config.microsoft.key = <key>
6+
# config.microsoft.secret = <secret>
7+
# ...
8+
#
9+
class Microsoft < Base
10+
include Protocols::Oauth2
11+
12+
attr_accessor :auth_url, :scope, :token_url, :user_info_url
13+
14+
def initialize
15+
super
16+
17+
@site = 'https://login.microsoftonline.com'
18+
@auth_url = '/common/oauth2/v2.0/authorize'
19+
@token_url = '/common/oauth2/v2.0/token'
20+
@user_info_url = 'https://graph.microsoft.com/v1.0/me'
21+
@scope = 'openid email https://graph.microsoft.com/User.Read'
22+
@state = SecureRandom.hex(16)
23+
end
24+
25+
def authorize_url(options = {})
26+
oauth_params = {
27+
client_id: @key,
28+
response_type: 'code'
29+
}
30+
options.merge!(oauth_params)
31+
super(options)
32+
end
33+
34+
def get_user_hash(access_token)
35+
response = access_token.get(user_info_url)
36+
37+
auth_hash(access_token).tap do |h|
38+
h[:user_info] = JSON.parse(response.body)
39+
h[:uid] = h[:user_info]['id']
40+
end
41+
end
42+
43+
# calculates and returns the url to which the user should be redirected,
44+
# to get authenticated at the external provider's site.
45+
def login_url(_params, _session)
46+
authorize_url(authorize_url: auth_url)
47+
end
48+
49+
# tries to login the user from access token
50+
def process_callback(params, _session)
51+
args = {}.tap do |a|
52+
a[:code] = params[:code] if params[:code]
53+
end
54+
55+
get_access_token(args, token_url: token_url, token_method: :post)
56+
end
57+
end
58+
end
59+
end

lib/sorcery/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module Sorcery
2-
VERSION = '0.10.1'
2+
VERSION = '0.10.2'
33
end

sorcery.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,5 @@ Gem::Specification.new do |s|
2929
s.add_development_dependency 'simplecov', '>= 0.3.8'
3030
s.add_development_dependency 'rspec-rails', '~> 3.5.0'
3131
s.add_development_dependency 'test-unit', '~> 3.1.0'
32+
s.add_development_dependency 'byebug', '~> 9.0.0'
3233
end

spec/controllers/controller_oauth2_spec.rb

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@
151151
expect(flash[:notice]).to eq 'Success!'
152152
end
153153

154-
[:github, :google, :liveid, :vk, :salesforce, :paypal, :slack, :wechat].each do |provider|
154+
[:github, :google, :liveid, :vk, :salesforce, :paypal, :slack, :wechat, :microsoft].each do |provider|
155155
describe "with #{provider}" do
156156
it 'login_at redirects correctly' do
157157
get :"login_at_test_#{provider}"
@@ -201,7 +201,7 @@
201201
end
202202

203203
sorcery_reload!([:user_activation,:external], :user_activation_mailer => ::SorceryMailer)
204-
sorcery_controller_property_set(:external_providers, [:facebook, :github, :google, :liveid, :vk, :salesforce, :paypal, :slack, :wechat])
204+
sorcery_controller_property_set(:external_providers, [:facebook, :github, :google, :liveid, :vk, :salesforce, :paypal, :slack, :wechat, :microsoft])
205205

206206
# TODO: refactor
207207
sorcery_controller_external_property_set(:facebook, :key, "eYVNBjBDi33aa9GkA3w")
@@ -231,6 +231,9 @@
231231
sorcery_controller_external_property_set(:wechat, :key, "eYVNBjBDi33aa9GkA3w")
232232
sorcery_controller_external_property_set(:wechat, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8")
233233
sorcery_controller_external_property_set(:wechat, :callback_url, "http://blabla.com")
234+
sorcery_controller_external_property_set(:microsoft, :key, "eYVNBjBDi33aa9GkA3w")
235+
sorcery_controller_external_property_set(:microsoft, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8")
236+
sorcery_controller_external_property_set(:microsoft, :callback_url, "http://blabla.com")
234237
end
235238

236239
after(:all) do
@@ -260,7 +263,7 @@
260263
expect(ActionMailer::Base.deliveries.size).to eq old_size
261264
end
262265

263-
[:github, :google, :liveid, :vk, :salesforce, :paypal, :wechat].each do |provider|
266+
[:github, :google, :liveid, :vk, :salesforce, :paypal, :wechat, :microsoft].each do |provider|
264267
it "does not send activation email to external users (#{provider})" do
265268
old_size = ActionMailer::Base.deliveries.size
266269
create_new_external_user provider
@@ -409,7 +412,7 @@ def stub_all_oauth2_requests!
409412
end
410413

411414
def set_external_property
412-
sorcery_controller_property_set(:external_providers, [:facebook, :github, :google, :liveid, :vk, :salesforce, :paypal, :slack, :wechat])
415+
sorcery_controller_property_set(:external_providers, [:facebook, :github, :google, :liveid, :vk, :salesforce, :paypal, :slack, :wechat, :microsoft])
413416
sorcery_controller_external_property_set(:facebook, :key, "eYVNBjBDi33aa9GkA3w")
414417
sorcery_controller_external_property_set(:facebook, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8")
415418
sorcery_controller_external_property_set(:facebook, :callback_url, "http://blabla.com")
@@ -437,6 +440,9 @@ def set_external_property
437440
sorcery_controller_external_property_set(:wechat, :key, "eYVNBjBDi33aa9GkA3w")
438441
sorcery_controller_external_property_set(:wechat, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8")
439442
sorcery_controller_external_property_set(:wechat, :callback_url, "http://blabla.com")
443+
sorcery_controller_external_property_set(:microsoft, :key, "eYVNBjBDi33aa9GkA3w")
444+
sorcery_controller_external_property_set(:microsoft, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8")
445+
sorcery_controller_external_property_set(:microsoft, :callback_url, "http://blabla.com")
440446
end
441447

442448
def provider_url(provider)
@@ -448,7 +454,8 @@ def provider_url(provider)
448454
vk: "https://oauth.vk.com/authorize?client_id=#{::Sorcery::Controller::Config.vk.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=#{::Sorcery::Controller::Config.vk.scope}&state",
449455
salesforce: "https://login.salesforce.com/services/oauth2/authorize?client_id=#{::Sorcery::Controller::Config.salesforce.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope#{'=' + ::Sorcery::Controller::Config.salesforce.scope unless ::Sorcery::Controller::Config.salesforce.scope.nil?}&state",
450456
slack: "https://slack.com/oauth/authorize?client_id=#{::Sorcery::Controller::Config.slack.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=identity.basic%2C+identity.email&state",
451-
wechat: "https://open.weixin.qq.com/connect/qrconnect?appid=#{::Sorcery::Controller::Config.wechat.key}&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=snsapi_login&state=#wechat_redirect"
457+
wechat: "https://open.weixin.qq.com/connect/qrconnect?appid=#{::Sorcery::Controller::Config.wechat.key}&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=snsapi_login&state=#wechat_redirect",
458+
microsoft: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=#{::Sorcery::Controller::Config.microsoft.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=openid+email+https%3A%2F%2Fgraph.microsoft.com%2FUser.Read&state"
452459
}[provider]
453460
end
454461
end

spec/rails_app/app/controllers/sorcery_controller.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ def login_at_test_wechat
104104
login_at(:wechat)
105105
end
106106

107+
def login_at_test_microsoft
108+
login_at(:microsoft)
109+
end
110+
107111
def login_at_test_google
108112
login_at(:google)
109113
end
@@ -174,6 +178,14 @@ def test_login_from_wechat
174178
end
175179
end
176180

181+
def test_login_from_microsoft
182+
if @user = login_from(:microsoft)
183+
redirect_to 'bla', notice: 'Success!'
184+
else
185+
redirect_to 'blu', alert: 'Failed!'
186+
end
187+
end
188+
177189
def test_login_from_google
178190
if @user = login_from(:google)
179191
redirect_to 'bla', notice: 'Success!'
@@ -272,6 +284,14 @@ def test_return_to_with_external_wechat
272284
end
273285
end
274286

287+
def test_return_to_with_external_microsoft
288+
if @user = login_from(:microsoft)
289+
redirect_back_or_to 'bla', notice: 'Success!'
290+
else
291+
redirect_to 'blu', alert: 'Failed!'
292+
end
293+
end
294+
275295
def test_return_to_with_external_google
276296
if @user = login_from(:google)
277297
redirect_back_or_to 'bla', notice: 'Success!'

spec/rails_app/config/routes.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
get :test_login_from_github
2323
get :test_login_from_paypal
2424
get :test_login_from_wechat
25+
get :test_login_from_microsoft
2526
get :test_login_from_google
2627
get :test_login_from_liveid
2728
get :test_login_from_vk
@@ -34,6 +35,7 @@
3435
get :login_at_test_github
3536
get :login_at_test_paypal
3637
get :login_at_test_wechat
38+
get :login_at_test_microsoft
3739
get :login_at_test_google
3840
get :login_at_test_liveid
3941
get :login_at_test_vk
@@ -46,6 +48,7 @@
4648
get :test_return_to_with_external_github
4749
get :test_return_to_with_external_paypal
4850
get :test_return_to_with_external_wechat
51+
get :test_return_to_with_external_microsoft
4952
get :test_return_to_with_external_google
5053
get :test_return_to_with_external_liveid
5154
get :test_return_to_with_external_vk

0 commit comments

Comments
 (0)