blob: 067ad818273a8cefd3e08801bc7d2cee13a688ca [file] [log] [blame]
margarethaa452c5e2018-04-25 22:48:09 +02001package de.ids_mannheim.korap.oauth2.service;
margaretha0e8f4e72018-04-05 14:11:52 +02002
margaretha6374f722018-04-17 18:45:57 +02003import java.util.HashMap;
4import java.util.Map;
margarethaf839dde2018-04-16 17:52:57 +02005import java.util.Set;
6
margarethafb027f92018-04-23 20:00:13 +02007import javax.ws.rs.core.Response.Status;
margarethaa0486272018-04-12 19:59:31 +02008
margarethaa0486272018-04-12 19:59:31 +02009import org.apache.oltu.oauth2.as.issuer.OAuthIssuer;
margarethafb027f92018-04-23 20:00:13 +020010import org.apache.oltu.oauth2.as.request.AbstractOAuthTokenRequest;
margarethaa0486272018-04-12 19:59:31 +020011import org.apache.oltu.oauth2.as.response.OAuthASResponse;
margarethaa0486272018-04-12 19:59:31 +020012import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
13import org.apache.oltu.oauth2.common.message.OAuthResponse;
margaretha0e8f4e72018-04-05 14:11:52 +020014import org.apache.oltu.oauth2.common.message.types.GrantType;
margarethaa0486272018-04-12 19:59:31 +020015import org.apache.oltu.oauth2.common.message.types.TokenType;
margaretha0e8f4e72018-04-05 14:11:52 +020016import org.springframework.beans.factory.annotation.Autowired;
17import org.springframework.stereotype.Service;
18
margaretha6374f722018-04-17 18:45:57 +020019import de.ids_mannheim.korap.config.Attributes;
margarethaa0486272018-04-12 19:59:31 +020020import de.ids_mannheim.korap.config.FullConfiguration;
margaretha0e8f4e72018-04-05 14:11:52 +020021import de.ids_mannheim.korap.exceptions.KustvaktException;
margaretha05122312018-04-16 15:01:34 +020022import de.ids_mannheim.korap.exceptions.StatusCodes;
margaretha6374f722018-04-17 18:45:57 +020023import de.ids_mannheim.korap.interfaces.AuthenticationManagerIface;
margarethaa452c5e2018-04-25 22:48:09 +020024import de.ids_mannheim.korap.oauth2.constant.OAuth2Error;
25import de.ids_mannheim.korap.oauth2.dao.AccessTokenDao;
26import de.ids_mannheim.korap.oauth2.entity.Authorization;
27import de.ids_mannheim.korap.oauth2.entity.OAuth2Client;
margaretha0e8f4e72018-04-05 14:11:52 +020028
29@Service
margarethab4ce6602018-04-26 20:23:57 +020030public class OAuth2TokenService {
margaretha0e8f4e72018-04-05 14:11:52 +020031
32 @Autowired
33 private OAuth2ClientService clientService;
margarethaa0486272018-04-12 19:59:31 +020034 @Autowired
margarethaa452c5e2018-04-25 22:48:09 +020035 private OAuth2AuthorizationService authorizationService;
36 @Autowired
37 private AccessTokenDao tokenDao;
38
39 @Autowired
margarethaa0486272018-04-12 19:59:31 +020040 private FullConfiguration config;
margaretha6374f722018-04-17 18:45:57 +020041 @Autowired
42 private AuthenticationManagerIface authenticationManager;
margarethafb027f92018-04-23 20:00:13 +020043 @Autowired
44 private OAuthIssuer oauthIssuer;
margaretha0e8f4e72018-04-05 14:11:52 +020045
margarethafb027f92018-04-23 20:00:13 +020046 public OAuthResponse requestAccessToken (
47 AbstractOAuthTokenRequest oAuthRequest)
margarethaf839dde2018-04-16 17:52:57 +020048 throws KustvaktException, OAuthSystemException {
49
50 String grantType = oAuthRequest.getGrantType();
margaretha0e8f4e72018-04-05 14:11:52 +020051
margaretha05122312018-04-16 15:01:34 +020052 if (grantType.equals(GrantType.AUTHORIZATION_CODE.toString())) {
margarethafb027f92018-04-23 20:00:13 +020053 return requestAccessTokenWithAuthorizationCode(
margarethaf839dde2018-04-16 17:52:57 +020054 oAuthRequest.getCode(), oAuthRequest.getRedirectURI(),
margarethafb027f92018-04-23 20:00:13 +020055 oAuthRequest.getClientId(), oAuthRequest.getClientSecret());
margaretha0e8f4e72018-04-05 14:11:52 +020056 }
margaretha05122312018-04-16 15:01:34 +020057 else if (grantType.equals(GrantType.PASSWORD.toString())) {
margarethafb027f92018-04-23 20:00:13 +020058 return requestAccessTokenWithPassword(oAuthRequest.getUsername(),
59 oAuthRequest.getPassword(), oAuthRequest.getScopes(),
60 oAuthRequest.getClientId(), oAuthRequest.getClientSecret());
margaretha0e8f4e72018-04-05 14:11:52 +020061 }
margaretha05122312018-04-16 15:01:34 +020062 else if (grantType.equals(GrantType.CLIENT_CREDENTIALS.toString())) {
margarethafb027f92018-04-23 20:00:13 +020063 return requestAccessTokenWithClientCredentials(
64 oAuthRequest.getClientId(), oAuthRequest.getClientSecret(),
margarethaf839dde2018-04-16 17:52:57 +020065 oAuthRequest.getScopes());
margaretha0e8f4e72018-04-05 14:11:52 +020066 }
67 else {
margaretha05122312018-04-16 15:01:34 +020068 throw new KustvaktException(StatusCodes.UNSUPPORTED_GRANT_TYPE,
69 grantType + " is not supported.",
margarethaa452c5e2018-04-25 22:48:09 +020070 OAuth2Error.UNSUPPORTED_GRANT_TYPE);
margaretha0e8f4e72018-04-05 14:11:52 +020071 }
72
73 }
margarethaa0486272018-04-12 19:59:31 +020074
margarethafb027f92018-04-23 20:00:13 +020075 /**
margarethaa452c5e2018-04-25 22:48:09 +020076 * RFC 6749:
77 * If the client type is confidential or the client was issued
78 * client credentials, the client MUST authenticate with the
79 * authorization server.
margarethaa0486272018-04-12 19:59:31 +020080 *
margarethaa0486272018-04-12 19:59:31 +020081 * @param authorizationCode
margarethaa452c5e2018-04-25 22:48:09 +020082 * @param redirectURI
83 * required if included in the authorization request
84 * @param clientId
85 * required if there is no authorization header
86 * @param clientSecret
87 * clilent_secret, required if client_secret was issued
88 * for the client in client registration.
margarethaa0486272018-04-12 19:59:31 +020089 * @return
90 * @throws OAuthSystemException
91 * @throws KustvaktException
margarethaa0486272018-04-12 19:59:31 +020092 */
93 private OAuthResponse requestAccessTokenWithAuthorizationCode (
margarethafb027f92018-04-23 20:00:13 +020094 String authorizationCode, String redirectURI, String clientId,
95 String clientSecret)
96 throws KustvaktException, OAuthSystemException {
97
98 clientService.authenticateClient(clientId, clientSecret);
margarethaa452c5e2018-04-25 22:48:09 +020099 authorizationService.verifyAuthorization(authorizationCode, clientId,
100 redirectURI);
margarethaa0486272018-04-12 19:59:31 +0200101
margarethafb027f92018-04-23 20:00:13 +0200102 return createsAccessTokenResponse();
margarethaa0486272018-04-12 19:59:31 +0200103 }
104
margaretha6374f722018-04-17 18:45:57 +0200105
margarethaa452c5e2018-04-25 22:48:09 +0200106 /**
107 * Third party apps must not be allowed to use password grant.
108 * MH: password grant is only allowed for trusted clients (korap
109 * frontend)
margaretha6374f722018-04-17 18:45:57 +0200110 *
margarethaa452c5e2018-04-25 22:48:09 +0200111 * According to RFC 6749, client authentication is only required
112 * for confidential clients and whenever client credentials are
113 * provided. Moreover, client_id is optional for password grant,
114 * but without it, the authentication server cannot check the
115 * client type. To make sure that confidential clients
116 * authenticate, client_id is made required (similar to
117 * authorization code grant).
margarethaa0486272018-04-12 19:59:31 +0200118 *
margarethafb027f92018-04-23 20:00:13 +0200119 *
margarethaa452c5e2018-04-25 22:48:09 +0200120 * @param username
121 * username, required
122 * @param password
123 * user password, required
margarethaf839dde2018-04-16 17:52:57 +0200124 * @param scopes
margarethaa452c5e2018-04-25 22:48:09 +0200125 * @param clientId
126 * client_id, required
127 * @param clientSecret
128 * clilent_secret, required if client_secret was issued
129 * for the client in client registration.
margarethaa0486272018-04-12 19:59:31 +0200130 * @return
margaretha6374f722018-04-17 18:45:57 +0200131 * @throws KustvaktException
margarethafb027f92018-04-23 20:00:13 +0200132 * @throws OAuthSystemException
margarethaa0486272018-04-12 19:59:31 +0200133 */
margarethafb027f92018-04-23 20:00:13 +0200134 private OAuthResponse requestAccessTokenWithPassword (String username,
135 String password, Set<String> scopes, String clientId,
136 String clientSecret)
137 throws KustvaktException, OAuthSystemException {
margarethaa0486272018-04-12 19:59:31 +0200138
margaretha6374f722018-04-17 18:45:57 +0200139 OAuth2Client client =
margarethafb027f92018-04-23 20:00:13 +0200140 clientService.authenticateClient(clientId, clientSecret);
margaretha6374f722018-04-17 18:45:57 +0200141 if (!client.isNative()) {
142 throw new KustvaktException(StatusCodes.CLIENT_AUTHORIZATION_FAILED,
143 "Password grant is not allowed for third party clients",
margarethaa452c5e2018-04-25 22:48:09 +0200144 OAuth2Error.UNAUTHORIZED_CLIENT);
margaretha6374f722018-04-17 18:45:57 +0200145 }
margarethaa0486272018-04-12 19:59:31 +0200146
margaretha6374f722018-04-17 18:45:57 +0200147 authenticateUser(username, password, scopes);
148 return createsAccessTokenResponse();
149 }
150
margarethafb027f92018-04-23 20:00:13 +0200151 public void authenticateUser (String username, String password,
margaretha6374f722018-04-17 18:45:57 +0200152 Set<String> scopes) throws KustvaktException {
153 if (username == null || username.isEmpty()) {
154 throw new KustvaktException(StatusCodes.MISSING_PARAMETER,
margarethaa452c5e2018-04-25 22:48:09 +0200155 "username is missing.", OAuth2Error.INVALID_REQUEST);
margaretha6374f722018-04-17 18:45:57 +0200156 }
157 if (password == null || password.isEmpty()) {
158 throw new KustvaktException(StatusCodes.MISSING_PARAMETER,
margarethaa452c5e2018-04-25 22:48:09 +0200159 "password is missing", OAuth2Error.INVALID_REQUEST);
margaretha6374f722018-04-17 18:45:57 +0200160 }
161
162 Map<String, Object> attributes = new HashMap<>();
163 if (scopes != null && !scopes.isEmpty()) {
164 attributes.put(Attributes.SCOPES, scopes);
165 }
166 authenticationManager.authenticate(
167 config.getOAuth2passwordAuthentication(), username, password,
168 attributes);
margarethaa0486272018-04-12 19:59:31 +0200169 }
170
margarethaa452c5e2018-04-25 22:48:09 +0200171 /**
172 * Clients must authenticate.
margarethaa0486272018-04-12 19:59:31 +0200173 *
margarethaa452c5e2018-04-25 22:48:09 +0200174 * @param clientId
175 * client_id parameter, required
176 * @param clientSecret
177 * client_secret parameter, required
margarethaf839dde2018-04-16 17:52:57 +0200178 * @param scopes
margarethaa0486272018-04-12 19:59:31 +0200179 * @return
margarethafb027f92018-04-23 20:00:13 +0200180 * @throws KustvaktException
181 * @throws OAuthSystemException
margarethaa0486272018-04-12 19:59:31 +0200182 */
183 private OAuthResponse requestAccessTokenWithClientCredentials (
margarethafb027f92018-04-23 20:00:13 +0200184 String clientId, String clientSecret, Set<String> scopes)
margarethaf839dde2018-04-16 17:52:57 +0200185 throws KustvaktException, OAuthSystemException {
margarethaa0486272018-04-12 19:59:31 +0200186
margarethafb027f92018-04-23 20:00:13 +0200187 if (clientSecret == null || clientSecret.isEmpty()) {
margaretha05122312018-04-16 15:01:34 +0200188 throw new KustvaktException(
189 StatusCodes.CLIENT_AUTHENTICATION_FAILED,
margarethaa452c5e2018-04-25 22:48:09 +0200190 "Missing parameters: client_secret",
191 OAuth2Error.INVALID_REQUEST);
margarethaa0486272018-04-12 19:59:31 +0200192 }
margarethafb027f92018-04-23 20:00:13 +0200193
194 clientService.authenticateClient(clientId, clientSecret);
195 return createsAccessTokenResponse();
margarethaa0486272018-04-12 19:59:31 +0200196 }
197
198
margarethaa452c5e2018-04-25 22:48:09 +0200199 /**
200 * Creates an OAuthResponse containing an access token and a
201 * refresh token with type Bearer.
margarethaa0486272018-04-12 19:59:31 +0200202 *
margarethafb027f92018-04-23 20:00:13 +0200203 * @return an OAuthResponse containing an access token
204 * @throws OAuthSystemException
margarethaa0486272018-04-12 19:59:31 +0200205 */
margarethaa452c5e2018-04-25 22:48:09 +0200206
margarethaf839dde2018-04-16 17:52:57 +0200207 private OAuthResponse createsAccessTokenResponse ()
208 throws OAuthSystemException {
margarethaa452c5e2018-04-25 22:48:09 +0200209 return createsAccessTokenResponse(null);
210 }
211
212 private OAuthResponse createsAccessTokenResponse (
213 Authorization authorization) throws OAuthSystemException {
margarethafb027f92018-04-23 20:00:13 +0200214 String accessToken = oauthIssuer.accessToken();
215 String refreshToken = oauthIssuer.refreshToken();
margarethaa0486272018-04-12 19:59:31 +0200216
margarethaa452c5e2018-04-25 22:48:09 +0200217 tokenDao.storeAccessToken(authorization, accessToken);
218
margarethafb027f92018-04-23 20:00:13 +0200219 OAuthResponse r =
220 OAuthASResponse.tokenResponse(Status.OK.getStatusCode())
221 .setAccessToken(accessToken)
222 .setTokenType(TokenType.BEARER.toString())
223 .setExpiresIn(String.valueOf(config.getTokenTTL()))
224 .setRefreshToken(refreshToken).buildJSONMessage();
margarethaf839dde2018-04-16 17:52:57 +0200225 // scope
margarethaa0486272018-04-12 19:59:31 +0200226 return r;
227 }
margaretha0e8f4e72018-04-05 14:11:52 +0200228}