forked from cordjs/core
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathOAuth2.coffee
210 lines (179 loc) · 8.04 KB
/
OAuth2.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
define [
'underscore'
'cord!isBrowser'
'cord!utils/Future'
'cord!utils/PasswordEncoder'
], (_, isBrowser, Future, PasswordEncoder) ->
class OAuth2
constructor: (serviceContainer, options) ->
@deferredRefreshTokenCallbacks = []
@refreshTokenRequested = false
defaultOptions =
clientId: ''
secretKey: ''
endpoints:
authorize: '/oauth/authorize'
accessToken: '/oauth/access_token'
@options = _.extend defaultOptions, options
@serviceContainer = serviceContainer
grantAccessTokenByAuhorizationCode: (code) =>
###
Получает токены по коду авторизации, ранее выданному авторизационным сервером
###
promise = Future.single('OAuth2::grantAccessTokenByAuthorizationCode promise')
@serviceContainer.eval 'request', (request) =>
params =
grant_type: 'authorization_code'
code: code
client_id: @options.clientId
client_secret: @options.secretKey
format: 'json'
redirect_uri: @options.endpoints.redirectUri
request.get @options.endpoints.accessToken, params, (result) =>
if result and result.access_token and result.refresh_token
promise.resolve(result.access_token, result.refresh_token)
else
promise.reject(new Error('No response from authorization server'))
promise
## Получение токена по grant_type = password (логин и пароль)
grantAccessTokenByPassword: (user, password, scope, callback) =>
params =
grant_type: 'password'
username: user
password: password
client_id: @options.clientId
scope: scope
json: true
@serviceContainer.eval 'request', (request) =>
request.get @options.endpoints.accessToken, params, (result) =>
if result
callback result.access_token, result.refresh_token
else
callback null, null
## Получение токена по grant_type = extension (например, одноразовый ключ)
grantAccessTokenByExtensions: (url, params, scope, callback) =>
requestParams =
grant_type: url
client_id: @options.clientId
scope: scope
json: true
requestParams = _.extend params, requestParams
@serviceContainer.eval 'request', (request) =>
request.get @options.endpoints.accessToken, requestParams, (result) =>
if result
callback result.access_token, result.refresh_token
else
callback null, null
clear: ->
@deferredRefreshTokenCallbacks = []
## Получение токена по grant_type = refresh_token (токен обновления)
grantAccessTokenByRefreshToken: (refreshToken, scope, callback) =>
@deferredRefreshTokenCallbacks.push callback if callback
params =
grant_type: 'refresh_token'
refresh_token: refreshToken
client_id: @options.clientId
scope: scope
if @refreshTokenRequested
_console.log "========================================================================"
_console.log "Refresh token already requested"
_console.log "========================================================================"
return if @refreshTokenRequested or @deferredRefreshTokenCallbacks.length == 0
@refreshTokenRequested = true
@serviceContainer.eval 'request', (request) =>
request.get @options.endpoints.accessToken, params, (result) =>
# Если порвалась связь, то не считаем протухшим рефреш токен
@refreshTokenRequested = false
if result && (result.access_token || result.error)
# Рефреш токен протух
callbackResult = true
for callback in @deferredRefreshTokenCallbacks
#Protection from multiple redirections
callbackResult &= callback result.access_token, result.refresh_token if callbackResult
@deferredRefreshTokenCallbacks = []
else
_console.log 'Cannot refresh token (('
setTimeout =>
_console.log 'Recall refresh token'
@grantAccessTokenByRefreshToken refreshToken
, 500
getAuthCodeWithoutPassword: ->
promise = Future.single('Api::getAuthCodeWithoutPassword promise')
if !isBrowser
promise.reject(new Error('It is only possible to get auth code at client side'))
Future.require('jquery').then ($) ->
params =
response_type: 'code'
client_id: global.config.oauth2.clientId
redirect_uri: global.config.oauth2.endpoints.redirectUri
format: 'json'
$.ajax
dataType: 'json',
url: global.config.oauth2.endpoints.authCodeWithoutLogin
data: params
xhrFields:
withCredentials: true
success: (data) =>
if data.code
promise.resolve(data.code)
else
if data.error is 'access_denied' and data.error_description is 'Not authorized'
promise.reject(new Error('Client is not authorized in authorization server'))
else
promise.reject(new Error('No auth code recieved. Response: '+JSON.stringify(data)))
error: (data) =>
promise.reject(new Error('Ajax request for auth code failed: ' + data.responseText))
promise
getAuthCodeByPassword: (login, password) ->
promise = Future.single('Api::getAuthCodeByPassword promise')
if !isBrowser
promise.reject(new Error('It is only possible to get auth code at client side'))
@getAuthEncoderByLogin(login).then (encoderData) =>
Future.require('jquery').then ($) ->
params =
response_type: 'code'
client_id: global.config.oauth2.clientId
redirect_uri: global.config.oauth2.endpoints.redirectUri
login: login
password: PasswordEncoder.encode(password, encoderData.algo, encoderData.salt, encoderData.clientSalt, encoderData.options)
format: 'json'
$.ajax
dataType: 'json',
url: global.config.oauth2.endpoints.authCode
data: params
xhrFields:
withCredentials: true
success: (data) =>
if data and data.code
promise.resolve(data.code)
else
if data.error is 'access_denied' and data.error_description is 'Not authorized'
promise.reject(new Error('Wrong login or password'))
else
promise.reject(new Error('No auth code recieved. Response:'+JSON.stringify(data)))
error: (data) =>
promise.reject(new Error('Ajax request for auth code failed: ' + data.responseText))
.catch (e) ->
promise.reject(new Error('Failed to get authEncoder. '+e.message))
promise
getAuthEncoderByLogin: (login) ->
promise = Future.single('Api::getAuthEncoder promise')
if !isBrowser
promise.reject(new Error('It is only possible to get authEncoder at client side'))
clientSalt = ''
clientSalt += Math.random().toString(36).slice(-8) for i in [0 .. 3]
Future.require('jquery').then ($) ->
$.ajax
url: global.config.oauth2.endpoints.authEncoder.replace('{login}', login).replace('{salt}', clientSalt)
xhrFields:
withCredentials: true
success: (data) =>
if not data.algo
promise.reject(new Error('No encode algo received'))
else if not data.salt
promise.reject(new Error('No encode salt received'))
else
promise.resolve({algo: data.algo, salt: data.salt, clientSalt: clientSalt, options: data.options || {}})
error: (data) =>
promise.reject(new Error('Ajax request for auth salt failed'+JSON.stringify(data)))
promise