/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF 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.apache.cxf.rs.security.oauth2.services; import java.net.URI; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import javax.ws.rs.core.Response; import org.apache.cxf.common.util.StringUtils; import org.apache.cxf.jaxrs.utils.HttpUtils; import org.apache.cxf.jaxrs.utils.JAXRSUtils; import org.apache.cxf.rs.security.oauth2.common.AbstractFormImplicitResponse; import org.apache.cxf.rs.security.oauth2.common.AccessTokenRegistration; import org.apache.cxf.rs.security.oauth2.common.Client; import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken; import org.apache.cxf.rs.security.oauth2.common.FormTokenResponse; import org.apache.cxf.rs.security.oauth2.common.OAuthRedirectionState; import org.apache.cxf.rs.security.oauth2.common.ServerAccessToken; import org.apache.cxf.rs.security.oauth2.common.UserSubject; import org.apache.cxf.rs.security.oauth2.provider.AccessTokenResponseFilter; import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants; import org.apache.cxf.rs.security.oauth2.utils.OAuthUtils; public abstract class AbstractImplicitGrantService extends RedirectionBasedGrantService { // For a client to validate that this client is a targeted recipient. private boolean reportClientId; private List<AccessTokenResponseFilter> responseHandlers = new LinkedList<AccessTokenResponseFilter>(); protected AbstractImplicitGrantService(String supportedResponseType, String supportedGrantType) { super(supportedResponseType, supportedGrantType); } protected AbstractImplicitGrantService(Set<String> supportedResponseTypes, String supportedGrantType) { super(supportedResponseTypes, supportedGrantType); } protected Response createGrant(OAuthRedirectionState state, Client client, List<String> requestedScope, List<String> approvedScope, UserSubject userSubject, ServerAccessToken preAuthorizedToken) { if (isFormResponse(state)) { return createHtmlResponse(prepareFormResponse(state, client, requestedScope, approvedScope, userSubject, preAuthorizedToken)); } else { StringBuilder sb = prepareRedirectResponse(state, client, requestedScope, approvedScope, userSubject, preAuthorizedToken); return Response.seeOther(URI.create(sb.toString())).build(); } } protected StringBuilder prepareRedirectResponse(OAuthRedirectionState state, Client client, List<String> requestedScope, List<String> approvedScope, UserSubject userSubject, ServerAccessToken preAuthorizedToken) { ClientAccessToken clientToken = getClientAccessToken(state, client, requestedScope, approvedScope, userSubject, preAuthorizedToken); // return the token by appending it as a fragment parameter to the redirect URI StringBuilder sb = getUriWithFragment(state.getRedirectUri()); sb.append(OAuthConstants.ACCESS_TOKEN).append("=").append(clientToken.getTokenKey()); sb.append("&"); sb.append(OAuthConstants.ACCESS_TOKEN_TYPE).append("=").append(clientToken.getTokenType()); if (isWriteOptionalParameters()) { sb.append("&").append(OAuthConstants.ACCESS_TOKEN_EXPIRES_IN) .append("=").append(clientToken.getExpiresIn()); if (!StringUtils.isEmpty(clientToken.getApprovedScope())) { sb.append("&").append(OAuthConstants.SCOPE).append("=") .append(HttpUtils.queryEncode(clientToken.getApprovedScope())); } for (Map.Entry<String, String> entry : clientToken.getParameters().entrySet()) { sb.append("&").append(entry.getKey()).append("=").append(HttpUtils.queryEncode(entry.getValue())); } } if (clientToken.getRefreshToken() != null) { processRefreshToken(sb, clientToken.getRefreshToken()); } finalizeResponse(sb, state); return sb; } protected AbstractFormImplicitResponse prepareFormResponse(OAuthRedirectionState state, Client client, List<String> requestedScope, List<String> approvedScope, UserSubject userSubject, ServerAccessToken preAuthorizedToken) { ClientAccessToken clientToken = getClientAccessToken(state, client, requestedScope, approvedScope, userSubject, preAuthorizedToken); FormTokenResponse bean = new FormTokenResponse(); bean.setResponseType(OAuthConstants.TOKEN_RESPONSE_TYPE); bean.setRedirectUri(state.getRedirectUri()); bean.setState(state.getState()); bean.setAccessToken(clientToken.getTokenKey()); bean.setAccessTokenType(clientToken.getTokenType()); bean.setAccessTokenExpiresIn(clientToken.getExpiresIn()); bean.getParameters().putAll(clientToken.getParameters()); return bean; } protected ClientAccessToken getClientAccessToken(OAuthRedirectionState state, Client client, List<String> requestedScope, List<String> approvedScope, UserSubject userSubject, ServerAccessToken preAuthorizedToken) { ServerAccessToken token = null; if (preAuthorizedToken == null) { AccessTokenRegistration reg = createTokenRegistration(state, client, requestedScope, approvedScope, userSubject); token = getDataProvider().createAccessToken(reg); } else { token = preAuthorizedToken; if (state.getNonce() != null) { JAXRSUtils.getCurrentMessage().getExchange().put(OAuthConstants.NONCE, state.getNonce()); } } ClientAccessToken clientToken = OAuthUtils.toClientAccessToken(token, isWriteOptionalParameters()); processClientAccessToken(clientToken, token); return clientToken; } protected AccessTokenRegistration createTokenRegistration(OAuthRedirectionState state, Client client, List<String> requestedScope, List<String> approvedScope, UserSubject userSubject) { AccessTokenRegistration reg = new AccessTokenRegistration(); reg.setClient(client); reg.setGrantType(super.getSupportedGrantType()); reg.setResponseType(state.getResponseType()); reg.setSubject(userSubject); reg.setRequestedScope(requestedScope); reg.setApprovedScope(getApprovedScope(requestedScope, approvedScope)); reg.setAudiences(Collections.singletonList(state.getAudience())); reg.setNonce(state.getNonce()); reg.getExtraProperties().putAll(state.getExtraProperties()); return reg; } protected void finalizeResponse(StringBuilder sb, OAuthRedirectionState state) { if (state.getState() != null) { sb.append("&"); String stateParam = state.getState(); sb.append(OAuthConstants.STATE).append("=").append(HttpUtils.urlEncode(stateParam)); } if (reportClientId) { sb.append("&").append(OAuthConstants.CLIENT_ID).append("=").append(state.getClientId()); } } protected void processRefreshToken(StringBuilder sb, String refreshToken) { LOG.warning("Implicit grant tokens MUST not have refresh tokens, refresh token will not be reported"); } protected void processClientAccessToken(ClientAccessToken clientToken, ServerAccessToken serverToken) { for (AccessTokenResponseFilter filter : responseHandlers) { filter.process(clientToken, serverToken); } } protected Response createErrorResponse(String state, String redirectUri, String error) { StringBuilder sb = getUriWithFragment(redirectUri); sb.append(OAuthConstants.ERROR_KEY).append("=").append(error); if (state != null) { sb.append("&"); sb.append(OAuthConstants.STATE).append("=").append(state); } return Response.seeOther(URI.create(sb.toString())).build(); } protected StringBuilder getUriWithFragment(String redirectUri) { StringBuilder sb = new StringBuilder(); sb.append(redirectUri); sb.append("#"); return sb; } public void setReportClientId(boolean reportClientId) { this.reportClientId = reportClientId; } public void setResponseFilters(List<AccessTokenResponseFilter> handlers) { this.responseHandlers = handlers; } public void setResponseFilter(AccessTokenResponseFilter responseHandler) { responseHandlers.add(responseHandler); } @Override protected boolean canRedirectUriBeEmpty(Client c) { return false; } @Override protected boolean canSupportPublicClient(Client c) { return true; } }