1+ package nl.myndocs.oauth2
2+
3+ import nl.myndocs.oauth2.authenticator.Authenticator
4+ import nl.myndocs.oauth2.authenticator.IdentityScopeVerifier
5+ import nl.myndocs.oauth2.client.ClientService
6+ import nl.myndocs.oauth2.exception.*
7+ import nl.myndocs.oauth2.identity.IdentityService
8+ import nl.myndocs.oauth2.identity.UserInfo
9+ import nl.myndocs.oauth2.request.*
10+ import nl.myndocs.oauth2.response.TokenResponse
11+ import nl.myndocs.oauth2.scope.ScopeParser
12+ import nl.myndocs.oauth2.token.AccessToken
13+ import nl.myndocs.oauth2.token.CodeToken
14+ import nl.myndocs.oauth2.token.TokenStore
15+ import nl.myndocs.oauth2.token.converter.AccessTokenConverter
16+ import nl.myndocs.oauth2.token.converter.CodeTokenConverter
17+ import nl.myndocs.oauth2.token.converter.RefreshTokenConverter
18+
19+ class Oauth2TokenService (
20+ private val identityService : IdentityService ,
21+ private val clientService : ClientService ,
22+ private val tokenStore : TokenStore ,
23+ private val accessTokenConverter : AccessTokenConverter ,
24+ private val refreshTokenConverter : RefreshTokenConverter ,
25+ private val codeTokenConverter : CodeTokenConverter
26+ ) : TokenService {
27+ private val INVALID_REQUEST_FIELD_MESSAGE = " '%s' field is missing"
28+ /* *
29+ * @throws InvalidIdentityException
30+ * @throws InvalidClientException
31+ * @throws InvalidScopeException
32+ */
33+ override fun authorize (passwordGrantRequest : PasswordGrantRequest ): TokenResponse {
34+ throwExceptionIfUnverifiedClient(passwordGrantRequest)
35+
36+ if (passwordGrantRequest.username == null ) {
37+ throw InvalidRequestException (INVALID_REQUEST_FIELD_MESSAGE .format(" username" ))
38+ }
39+
40+ if (passwordGrantRequest.password == null ) {
41+ throw InvalidRequestException (INVALID_REQUEST_FIELD_MESSAGE .format(" password" ))
42+ }
43+
44+ val requestedClient = clientService.clientOf(
45+ passwordGrantRequest.clientId!!
46+ )!!
47+ val requestedIdentity = identityService.identityOf(
48+ requestedClient, passwordGrantRequest.username
49+ )
50+
51+ if (requestedIdentity == null || ! identityService.validCredentials(requestedClient, requestedIdentity, passwordGrantRequest.password)) {
52+ throw InvalidIdentityException ()
53+ }
54+
55+ var requestedScopes = ScopeParser .parseScopes(passwordGrantRequest.scope)
56+ .toSet()
57+
58+ if (passwordGrantRequest.scope == null ) {
59+ requestedScopes = requestedClient.clientScopes
60+ }
61+
62+ val scopesAllowed = scopesAllowed(requestedClient.clientScopes, requestedScopes)
63+
64+ if (! scopesAllowed) {
65+ throw InvalidScopeException (requestedScopes.minus(requestedClient.clientScopes))
66+ }
67+
68+ if (! identityService.validScopes(requestedClient, requestedIdentity, requestedScopes)) {
69+ throw InvalidScopeException (requestedScopes)
70+ }
71+
72+ val accessToken = accessTokenConverter.convertToToken(
73+ requestedIdentity.username,
74+ requestedClient.clientId,
75+ requestedScopes,
76+ refreshTokenConverter.convertToToken(
77+ requestedIdentity.username,
78+ requestedClient.clientId,
79+ requestedScopes
80+ )
81+ )
82+
83+ tokenStore.storeAccessToken(accessToken)
84+
85+ return accessToken.toTokenResponse()
86+ }
87+
88+ override fun authorize (authorizationCodeRequest : AuthorizationCodeRequest ): TokenResponse {
89+ throwExceptionIfUnverifiedClient(authorizationCodeRequest)
90+
91+ if (authorizationCodeRequest.code == null ) {
92+ throw InvalidRequestException (INVALID_REQUEST_FIELD_MESSAGE .format(" code" ))
93+ }
94+
95+ if (authorizationCodeRequest.redirectUri == null ) {
96+ throw InvalidRequestException (INVALID_REQUEST_FIELD_MESSAGE .format(" redirect_uri" ))
97+ }
98+
99+ val consumeCodeToken = tokenStore.consumeCodeToken(authorizationCodeRequest.code)
100+ ? : throw InvalidGrantException ()
101+
102+
103+ if (consumeCodeToken.redirectUri != authorizationCodeRequest.redirectUri || consumeCodeToken.clientId != authorizationCodeRequest.clientId) {
104+ throw InvalidGrantException ()
105+ }
106+
107+ val accessToken = accessTokenConverter.convertToToken(
108+ consumeCodeToken.username,
109+ consumeCodeToken.clientId,
110+ consumeCodeToken.scopes,
111+ refreshTokenConverter.convertToToken(
112+ consumeCodeToken.username,
113+ consumeCodeToken.clientId,
114+ consumeCodeToken.scopes
115+ )
116+ )
117+
118+ tokenStore.storeAccessToken(accessToken)
119+
120+ return accessToken.toTokenResponse()
121+ }
122+
123+ override fun refresh (refreshTokenRequest : RefreshTokenRequest ): TokenResponse {
124+ throwExceptionIfUnverifiedClient(refreshTokenRequest)
125+
126+ if (refreshTokenRequest.refreshToken == null ) {
127+ throw InvalidRequestException (INVALID_REQUEST_FIELD_MESSAGE .format(" refresh_token" ))
128+ }
129+
130+ val refreshToken = tokenStore.refreshToken(refreshTokenRequest.refreshToken) ? : throw InvalidGrantException ()
131+
132+ if (refreshToken.clientId != refreshTokenRequest.clientId) {
133+ throw InvalidGrantException ()
134+ }
135+
136+ val accessToken = accessTokenConverter.convertToToken(
137+ refreshToken.username,
138+ refreshToken.clientId,
139+ refreshToken.scopes,
140+ refreshToken
141+ )
142+
143+ tokenStore.storeAccessToken(accessToken)
144+
145+ return accessToken.toTokenResponse()
146+ }
147+
148+ override fun redirect (
149+ redirect : RedirectAuthorizationCodeRequest ,
150+ authenticator : Authenticator ? ,
151+ identityScopeVerifier : IdentityScopeVerifier ?
152+ ): CodeToken {
153+ if (redirect.clientId == null ) {
154+ throw InvalidRequestException (INVALID_REQUEST_FIELD_MESSAGE .format(" client_id" ))
155+ }
156+
157+ if (redirect.username == null ) {
158+ throw InvalidRequestException (INVALID_REQUEST_FIELD_MESSAGE .format(" username" ))
159+ }
160+
161+ if (redirect.password == null ) {
162+ throw InvalidRequestException (INVALID_REQUEST_FIELD_MESSAGE .format(" password" ))
163+ }
164+ if (redirect.redirectUri == null ) {
165+ throw InvalidRequestException (INVALID_REQUEST_FIELD_MESSAGE .format(" redirect_uri" ))
166+ }
167+
168+ val clientOf = clientService.clientOf(redirect.clientId) ? : throw InvalidClientException ()
169+
170+ if (! clientOf.redirectUris.contains(redirect.redirectUri)) {
171+ throw InvalidGrantException (" invalid 'redirect_uri'" )
172+ }
173+
174+ val identityOf = identityService.identityOf(clientOf, redirect.username) ? : throw InvalidIdentityException ()
175+
176+ var validIdentity = authenticator?.validCredentials(clientOf, identityOf, redirect.password)
177+ ? : identityService.validCredentials(clientOf, identityOf, redirect.password)
178+
179+ if (! validIdentity) {
180+ throw InvalidIdentityException ()
181+ }
182+
183+ var requestedScopes = ScopeParser .parseScopes(redirect.scope)
184+
185+ if (redirect.scope == null ) {
186+ requestedScopes = clientOf.clientScopes
187+ }
188+
189+ val scopesAllowed = identityScopeVerifier?.validScopes(clientOf, identityOf, requestedScopes)
190+ ? : scopesAllowed(clientOf.clientScopes, requestedScopes)
191+
192+ if (! scopesAllowed) {
193+ throw InvalidScopeException (requestedScopes.minus(clientOf.clientScopes))
194+ }
195+
196+ if (! identityService.validScopes(clientOf, identityOf, requestedScopes)) {
197+ throw InvalidScopeException (requestedScopes)
198+ }
199+
200+ val codeToken = codeTokenConverter.convertToToken(
201+ identityOf.username,
202+ clientOf.clientId,
203+ redirect.redirectUri,
204+ requestedScopes
205+ )
206+
207+ tokenStore.storeCodeToken(codeToken)
208+
209+ return codeToken
210+ }
211+
212+ override fun redirect (
213+ redirect : RedirectTokenRequest ,
214+ authenticator : Authenticator ? ,
215+ identityScopeVerifier : IdentityScopeVerifier ?
216+ ): AccessToken {
217+ if (redirect.clientId == null ) {
218+ throw InvalidRequestException (INVALID_REQUEST_FIELD_MESSAGE .format(" client_id" ))
219+ }
220+
221+ if (redirect.username == null ) {
222+ throw InvalidRequestException (INVALID_REQUEST_FIELD_MESSAGE .format(" username" ))
223+ }
224+
225+ if (redirect.password == null ) {
226+ throw InvalidRequestException (INVALID_REQUEST_FIELD_MESSAGE .format(" password" ))
227+ }
228+ if (redirect.redirectUri == null ) {
229+ throw InvalidRequestException (INVALID_REQUEST_FIELD_MESSAGE .format(" redirect_uri" ))
230+ }
231+
232+ val clientOf = clientService.clientOf(redirect.clientId) ? : throw InvalidClientException ()
233+
234+ if (! clientOf.redirectUris.contains(redirect.redirectUri)) {
235+ throw InvalidGrantException (" invalid 'redirect_uri'" )
236+ }
237+
238+ val identityOf = identityService.identityOf(clientOf, redirect.username) ? : throw InvalidIdentityException ()
239+
240+ var validIdentity = authenticator?.validCredentials(clientOf, identityOf, redirect.password)
241+ ? : identityService.validCredentials(clientOf, identityOf, redirect.password)
242+
243+ if (! validIdentity) {
244+ throw InvalidIdentityException ()
245+ }
246+
247+ var requestedScopes = ScopeParser .parseScopes(redirect.scope)
248+
249+ if (redirect.scope == null ) {
250+ requestedScopes = clientOf.clientScopes
251+ }
252+
253+ val scopesAllowed = identityScopeVerifier?.validScopes(clientOf, identityOf, requestedScopes)
254+ ? : scopesAllowed(clientOf.clientScopes, requestedScopes)
255+ if (! scopesAllowed) {
256+ throw InvalidScopeException (requestedScopes.minus(clientOf.clientScopes))
257+ }
258+
259+ if (! identityService.validScopes(clientOf, identityOf, requestedScopes)) {
260+ throw InvalidScopeException (requestedScopes)
261+ }
262+
263+ val accessToken = accessTokenConverter.convertToToken(
264+ identityOf.username,
265+ clientOf.clientId,
266+ requestedScopes,
267+ null
268+ )
269+
270+ tokenStore.storeAccessToken(accessToken)
271+
272+ return accessToken
273+ }
274+
275+ override fun userInfo (accessToken : String ): UserInfo {
276+ val storedAccessToken = tokenStore.accessToken(accessToken)!!
277+ val client = clientService.clientOf(storedAccessToken.clientId)!!
278+ val identity = identityService.identityOf(client, storedAccessToken.username)!!
279+
280+ return UserInfo (
281+ identity,
282+ client,
283+ storedAccessToken.scopes
284+ )
285+ }
286+
287+ private fun throwExceptionIfUnverifiedClient (clientRequest : ClientRequest ) {
288+ if (clientRequest.clientId == null ) {
289+ throw InvalidRequestException (INVALID_REQUEST_FIELD_MESSAGE .format(" client_id" ))
290+ }
291+
292+ if (clientRequest.clientSecret == null ) {
293+ throw InvalidRequestException (INVALID_REQUEST_FIELD_MESSAGE .format(" client_secret" ))
294+ }
295+
296+ val client = clientService.clientOf(clientRequest.clientId!! ) ? : throw InvalidClientException ()
297+
298+ if (! clientService.validClient(client, clientRequest.clientSecret!! )) {
299+ throw InvalidClientException ()
300+ }
301+ }
302+
303+ private fun scopesAllowed (clientScopes : Set <String >, requestedScopes : Set <String >): Boolean {
304+ return clientScopes.containsAll(requestedScopes)
305+ }
306+
307+ private fun AccessToken.toTokenResponse () = TokenResponse (
308+ accessToken,
309+ tokenType,
310+ expiresIn(),
311+ refreshToken?.refreshToken
312+ )
313+ }
0 commit comments