/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2013-2016 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package org.glassfish.jersey.tests.e2e.oauth;
import java.io.IOException;
import java.util.Collection;
import java.util.logging.Logger;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import org.glassfish.jersey.client.oauth2.ClientIdentifier;
import org.glassfish.jersey.client.oauth2.OAuth2ClientSupport;
import org.glassfish.jersey.client.oauth2.OAuth2CodeGrantFlow;
import org.glassfish.jersey.client.oauth2.OAuth2Parameters;
import org.glassfish.jersey.client.oauth2.TokenResult;
import org.glassfish.jersey.logging.LoggingFeature;
import org.glassfish.jersey.moxy.json.MoxyJsonFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
/**
* Tests OAuth 2 client.
*
* @author Miroslav Fuksa
*/
public class OAuth2Test extends JerseyTest {
private static final String STATE = "4564dsf54654fsda654af";
private static final String CODE = "code-xyz";
private static final String CLIENT_PUBLIC = "clientPublic";
private static final String CLIENT_SECRET = "clientSecret";
@Override
protected Application configure() {
return new ResourceConfig(MoxyJsonFeature.class, AuthorizationResource.class)
.register(new LoggingFeature(Logger.getAnonymousLogger(), LoggingFeature.Verbosity.PAYLOAD_ANY));
}
@Path("oauth")
public static class AuthorizationResource {
@POST
@Path("access-token")
@Produces(MediaType.APPLICATION_JSON)
public MyTokenResult getAccessToken(@FormParam("grant_type") String grantType,
@FormParam("code") String code,
@FormParam("redirect_uri") String redirectUri,
@FormParam("client_id") String clientId) {
try {
assertEquals("authorization_code", grantType);
assertEquals("urn:ietf:wg:oauth:2.0:oob", redirectUri);
assertEquals(CODE, code);
assertEquals(CLIENT_PUBLIC, clientId);
} catch (AssertionError e) {
e.printStackTrace();
throw new BadRequestException(Response.status(400).entity(e.getMessage()).build());
}
final MyTokenResult myTokenResult = new MyTokenResult();
myTokenResult.setAccessToken("access-token-aab999f");
myTokenResult.setExpiresIn("3600");
myTokenResult.setTokenType("access-token");
myTokenResult.setRefreshToken("refresh-xyz");
return myTokenResult;
}
@GET
@Path("authorization")
public String authorization(@QueryParam("state") String state,
@QueryParam("response_type") String responseType,
@QueryParam("scope") String scope,
@QueryParam("readOnly") String readOnly,
@QueryParam("redirect_uri") String redirectUri) {
try {
assertEquals("code", responseType);
assertEquals(STATE, state);
assertEquals("urn:ietf:wg:oauth:2.0:oob", redirectUri);
assertEquals("contact", scope);
assertEquals("true", readOnly);
} catch (AssertionError e) {
e.printStackTrace();
throw new BadRequestException(Response.status(400).entity(e.getMessage()).build());
}
return CODE;
}
@POST
@Path("refresh-token")
@Produces(MediaType.APPLICATION_JSON)
public String refreshToken(@FormParam("grant_type") String grantType,
@FormParam("refresh_token") String refreshToken,
@HeaderParam("isArray") @DefaultValue("false") boolean isArray) {
try {
assertEquals("refresh_token", grantType);
assertEquals("refresh-xyz", refreshToken);
} catch (AssertionError e) {
e.printStackTrace();
throw new BadRequestException(Response.status(400).entity(e.getMessage()).build());
}
return isArray
? "{\"access_token\":[\"access-token-new\"],\"expires_in\":\"3600\",\"token_type\":\"access-token\"}"
: "{\"access_token\":\"access-token-new\",\"expires_in\":\"3600\",\"token_type\":\"access-token\"}";
}
}
@Test
public void testFlow() {
testFlow(false);
}
@Test
public void testFlowWithArrayInResponse() {
testFlow(true);
}
private void testFlow(final boolean isArray) {
ClientIdentifier clientId = new ClientIdentifier(CLIENT_PUBLIC, CLIENT_SECRET);
final String authUri = UriBuilder.fromUri(getBaseUri()).path("oauth").path("authorization").build().toString();
final String accessTokenUri = UriBuilder.fromUri(getBaseUri()).path("oauth").path("access-token").build().toString();
final String refreshTokenUri = UriBuilder.fromUri(getBaseUri()).path("oauth").path("refresh-token").build().toString();
final String state = STATE;
final Client client = ClientBuilder.newClient();
if (isArray) {
client.register(new ClientRequestFilter() {
@Override
public void filter(final ClientRequestContext requestContext) throws IOException {
requestContext.getHeaders().putSingle("isArray", true);
}
});
}
final OAuth2CodeGrantFlow.Builder builder =
OAuth2ClientSupport.authorizationCodeGrantFlowBuilder(clientId, authUri, accessTokenUri);
final OAuth2CodeGrantFlow flow = builder
.client(client)
.refreshTokenUri(refreshTokenUri)
.property(OAuth2CodeGrantFlow.Phase.AUTHORIZATION, "readOnly", "true")
.property(OAuth2CodeGrantFlow.Phase.AUTHORIZATION, OAuth2Parameters.STATE, state)
.scope("contact")
.build();
final String finalAuthorizationUri = flow.start();
final Response response = ClientBuilder.newClient().target(finalAuthorizationUri).request().get();
assertEquals(200, response.getStatus());
final String code = response.readEntity(String.class);
assertEquals(CODE, code);
final TokenResult result = flow.finish(code, state);
assertEquals("access-token-aab999f", result.getAccessToken());
assertEquals(new Long(3600), result.getExpiresIn());
assertEquals("access-token", result.getTokenType());
final TokenResult refreshResult = flow.refreshAccessToken(result.getRefreshToken());
assertEquals("access-token-new", refreshResult.getAccessToken());
assertEquals(new Long(3600), refreshResult.getExpiresIn());
assertEquals("access-token", refreshResult.getTokenType());
if (isArray) {
final Collection<String> array = (Collection<String>) refreshResult.getAllProperties().get("access_token");
assertThat(array.size(), is(1));
assertThat(array, hasItem("access-token-new"));
}
}
@XmlRootElement
public static class MyTokenResult {
@XmlAttribute(name = "access_token")
private String accessToken;
@XmlAttribute(name = "expires_in")
private String expiresIn;
@XmlAttribute(name = "token_type")
private String tokenType;
public String getRefreshToken() {
return refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
@XmlAttribute(name = "refresh_token")
private String refreshToken;
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public String getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(String expiresIn) {
this.expiresIn = expiresIn;
}
public String getTokenType() {
return tokenType;
}
public void setTokenType(String tokenType) {
this.tokenType = tokenType;
}
}
}