/* * Copyright (c) 2013, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.wso2.carbon.identity.oauth.endpoint.token; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.oltu.oauth2.as.response.OAuthASResponse; import org.apache.oltu.oauth2.as.response.OAuthASResponse.OAuthTokenResponseBuilder; import org.apache.oltu.oauth2.common.OAuth; import org.apache.oltu.oauth2.common.exception.OAuthProblemException; import org.apache.oltu.oauth2.common.exception.OAuthSystemException; import org.apache.oltu.oauth2.common.message.OAuthResponse; import org.apache.oltu.oauth2.common.message.types.GrantType; import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.identity.oauth.common.OAuth2ErrorCodes; import org.wso2.carbon.identity.oauth.common.OAuthConstants; import org.wso2.carbon.identity.oauth.common.exception.OAuthClientException; import org.wso2.carbon.identity.oauth.endpoint.OAuthRequestWrapper; import org.wso2.carbon.identity.oauth.endpoint.util.EndpointUtil; import org.wso2.carbon.identity.oauth2.ResponseHeader; import org.wso2.carbon.identity.oauth2.dto.OAuth2AccessTokenReqDTO; import org.wso2.carbon.identity.oauth2.dto.OAuth2AccessTokenRespDTO; import org.wso2.carbon.identity.oauth2.model.CarbonOAuthTokenRequest; import org.wso2.carbon.utils.multitenancy.MultitenantConstants; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.ResponseBuilder; import java.util.Enumeration; @Path("/token") public class OAuth2TokenEndpoint { private static final Log log = LogFactory.getLog(OAuth2TokenEndpoint.class); public static final String BEARER = "Bearer"; @POST @Path("/") @Consumes("application/x-www-form-urlencoded") @Produces("application/json") public Response issueAccessToken(@Context HttpServletRequest request, MultivaluedMap<String, String> paramMap) throws OAuthSystemException { try { PrivilegedCarbonContext.startTenantFlow(); PrivilegedCarbonContext carbonContext = PrivilegedCarbonContext .getThreadLocalCarbonContext(); carbonContext.setTenantId(MultitenantConstants.SUPER_TENANT_ID); carbonContext.setTenantDomain(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME); HttpServletRequestWrapper httpRequest = new OAuthRequestWrapper(request, paramMap); // extract the basic auth credentials if present in the request and use for // authentication. if (request.getHeader(OAuthConstants.HTTP_REQ_HEADER_AUTHZ) != null) { try { String[] clientCredentials = EndpointUtil.extractCredentialsFromAuthzHeader( request.getHeader(OAuthConstants.HTTP_REQ_HEADER_AUTHZ)); // The client MUST NOT use more than one authentication method in each request if (paramMap.containsKey(OAuth.OAUTH_CLIENT_ID) && paramMap.containsKey(OAuth.OAUTH_CLIENT_SECRET)) { return handleBasicAuthFailure(); } //If a client sends an invalid base64 encoded clientid:clientsecret value, it results in this //array to only contain 1 element. This happens on specific errors though. if (clientCredentials.length != 2) { return handleBasicAuthFailure(); } // add the credentials available in Authorization header to the parameter map paramMap.add(OAuth.OAUTH_CLIENT_ID, clientCredentials[0]); paramMap.add(OAuth.OAUTH_CLIENT_SECRET, clientCredentials[1]); } catch (OAuthClientException e) { // malformed credential string is considered as an auth failure. log.error("Error while extracting credentials from authorization header", e); return handleBasicAuthFailure(); } } try { CarbonOAuthTokenRequest oauthRequest = new CarbonOAuthTokenRequest(httpRequest); // exchange the access token for the authorization grant. OAuth2AccessTokenRespDTO oauth2AccessTokenResp = getAccessToken(oauthRequest); // if there BE has returned an error if (oauth2AccessTokenResp.getErrorMsg() != null) { // if there is an auth failure, HTTP 401 Status Code should be sent back to the client. if (OAuth2ErrorCodes.INVALID_CLIENT.equals(oauth2AccessTokenResp.getErrorCode())) { return handleBasicAuthFailure(); } else if ("sql_error".equals(oauth2AccessTokenResp.getErrorCode())){ return handleSQLError(); } else if (OAuth2ErrorCodes.SERVER_ERROR.equals(oauth2AccessTokenResp.getErrorCode())) { return handleServerError(); } else { // Otherwise send back HTTP 400 Status Code OAuthResponse.OAuthErrorResponseBuilder oAuthErrorResponseBuilder = OAuthASResponse .errorResponse(HttpServletResponse.SC_BAD_REQUEST) .setError(oauth2AccessTokenResp.getErrorCode()) .setErrorDescription(oauth2AccessTokenResp.getErrorMsg()); OAuthResponse response = oAuthErrorResponseBuilder.buildJSONMessage(); ResponseHeader[] headers = oauth2AccessTokenResp.getResponseHeaders(); ResponseBuilder respBuilder = Response .status(response.getResponseStatus()); if (headers != null && headers.length > 0) { for (int i = 0; i < headers.length; i++) { if (headers[i] != null) { respBuilder.header(headers[i].getKey(), headers[i].getValue()); } } } return respBuilder.entity(response.getBody()).build(); } } else { OAuthTokenResponseBuilder oAuthRespBuilder = OAuthASResponse .tokenResponse(HttpServletResponse.SC_OK) .setAccessToken(oauth2AccessTokenResp.getAccessToken()) .setRefreshToken(oauth2AccessTokenResp.getRefreshToken()) .setExpiresIn(Long.toString(oauth2AccessTokenResp.getExpiresIn())) .setTokenType(BEARER); oAuthRespBuilder.setScope(oauth2AccessTokenResp.getAuthorizedScopes()); // OpenID Connect ID token if (oauth2AccessTokenResp.getIDToken() != null) { oAuthRespBuilder.setParam(OAuthConstants.ID_TOKEN, oauth2AccessTokenResp.getIDToken()); } OAuthResponse response = oAuthRespBuilder.buildJSONMessage(); ResponseHeader[] headers = oauth2AccessTokenResp.getResponseHeaders(); ResponseBuilder respBuilder = Response .status(response.getResponseStatus()) .header(OAuthConstants.HTTP_RESP_HEADER_CACHE_CONTROL, OAuthConstants.HTTP_RESP_HEADER_VAL_CACHE_CONTROL_NO_STORE) .header(OAuthConstants.HTTP_RESP_HEADER_PRAGMA, OAuthConstants.HTTP_RESP_HEADER_VAL_PRAGMA_NO_CACHE); if (headers != null && headers.length > 0) { for (int i = 0; i < headers.length; i++) { if (headers[i] != null) { respBuilder.header(headers[i].getKey(), headers[i].getValue()); } } } return respBuilder.entity(response.getBody()).build(); } } catch (OAuthProblemException e) { log.error("Error while creating the Carbon OAuth token request", e); OAuthResponse res = OAuthASResponse .errorResponse(HttpServletResponse.SC_BAD_REQUEST).error(e) .buildJSONMessage(); return Response.status(res.getResponseStatus()).entity(res.getBody()).build(); } } finally { PrivilegedCarbonContext.endTenantFlow(); } } private Response handleBasicAuthFailure() throws OAuthSystemException { OAuthResponse response = OAuthASResponse.errorResponse(HttpServletResponse.SC_UNAUTHORIZED) .setError(OAuth2ErrorCodes.INVALID_CLIENT) .setErrorDescription("Client Authentication failed.").buildJSONMessage(); return Response.status(response.getResponseStatus()) .header(OAuthConstants.HTTP_RESP_HEADER_AUTHENTICATE, EndpointUtil.getRealmInfo()) .entity(response.getBody()).build(); } private Response handleServerError() throws OAuthSystemException { OAuthResponse response = OAuthASResponse.errorResponse(HttpServletResponse.SC_INTERNAL_SERVER_ERROR). setError(OAuth2ErrorCodes.SERVER_ERROR).setErrorDescription("Internal Server Error.").buildJSONMessage(); return Response.status(response.getResponseStatus()).header(OAuthConstants.HTTP_RESP_HEADER_AUTHENTICATE, EndpointUtil.getRealmInfo()).entity(response.getBody()).build(); } private Response handleSQLError() throws OAuthSystemException { OAuthResponse response = OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_GATEWAY). setError(OAuth2ErrorCodes.SERVER_ERROR).setErrorDescription("Service Unavailable Error.").buildJSONMessage(); return Response.status(response.getResponseStatus()).header(OAuthConstants.HTTP_RESP_HEADER_AUTHENTICATE, EndpointUtil.getRealmInfo()).entity(response.getBody()).build(); } private OAuth2AccessTokenRespDTO getAccessToken(CarbonOAuthTokenRequest oauthRequest) { OAuth2AccessTokenReqDTO tokenReqDTO = new OAuth2AccessTokenReqDTO(); String grantType = oauthRequest.getGrantType(); tokenReqDTO.setGrantType(grantType); tokenReqDTO.setClientId(oauthRequest.getClientId()); tokenReqDTO.setClientSecret(oauthRequest.getClientSecret()); tokenReqDTO.setCallbackURI(oauthRequest.getRedirectURI()); tokenReqDTO.setScope(oauthRequest.getScopes().toArray(new String[oauthRequest.getScopes().size()])); tokenReqDTO.setTenantDomain(oauthRequest.getTenantDomain()); // Check the grant type and set the corresponding parameters if (GrantType.AUTHORIZATION_CODE.toString().equals(grantType)) { tokenReqDTO.setAuthorizationCode(oauthRequest.getCode()); } else if (GrantType.PASSWORD.toString().equals(grantType)) { tokenReqDTO.setResourceOwnerUsername(oauthRequest.getUsername()); tokenReqDTO.setResourceOwnerPassword(oauthRequest.getPassword()); } else if (GrantType.REFRESH_TOKEN.toString().equals(grantType)) { tokenReqDTO.setRefreshToken(oauthRequest.getRefreshToken()); } else if (org.wso2.carbon.identity.oauth.common.GrantType.SAML20_BEARER.toString().equals(grantType)) { tokenReqDTO.setAssertion(oauthRequest.getAssertion()); } else if (org.wso2.carbon.identity.oauth.common.GrantType.IWA_NTLM.toString().equals(grantType)) { tokenReqDTO.setWindowsToken(oauthRequest.getWindowsToken()); } else { // Set all request parameters to the OAuth2AccessTokenReqDTO tokenReqDTO.setRequestParameters(oauthRequest.getRequestParameters()); } return EndpointUtil.getOAuth2Service().issueAccessToken(tokenReqDTO); } }