Coming Up for Air

A Simple OAuth2 Client and Server Example: Part II

In the last post, we took a look at the server side of our OAuth2 system. In this post, we’ll take a quick look at the unit tests that will act as TheUser.

Let’s get right to the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
@RunAsClient
public class AuthTest extends Arquillian {

    @ArquillianResource
    private URL url;
    private Client client = JerseyClientBuilder.newClient();

    @Deployment
    public static WebArchive createDeployment() {
        WebArchive archive = ShrinkWrap.create(WebArchive.class)
                .addPackages(true, "com.steeplesoft.oauth2")
                .addAsWebInfResource(
                    new FileAsset(
                        new File("src/main/webapp/WEB-INF/beans.xml")),
                            "beans.xml")
                .addAsWebInfResource(
                    new FileAsset(
                        new File("src/main/webapp/WEB-INF/web.xml")),
                            "web.xml")
                .addAsLibraries(Maven.resolver()
                    .loadPomFromFile("pom.xml")
                    .importRuntimeDependencies()
                    .resolve()
                    .withTransitivity()
                    .asFile());
        return archive;
    }

    @Test
    public void authorizationRequest() {
        try {
            Response response = makeAuthCodeRequest();
            Assert.assertEquals(Status.OK.getStatusCode(),
                response.getStatus());

            String authCode = getAuthCode(response);
            Assert.assertNotNull(authCode);
        } catch (OAuthSystemException |
                URISyntaxException |
                JSONException ex) {
            Logger.getLogger(AuthTest.class.getName())
                .log(Level.SEVERE, null, ex);
        }
    }

    @Test
    public void authCodeTokenRequest() throws OAuthSystemException {
        try {
            Response response = makeAuthCodeRequest();
            Assert.assertEquals(Status.OK.getStatusCode(),
                response.getStatus());

            String authCode = getAuthCode(response);
            Assert.assertNotNull(authCode);
            OAuthAccessTokenResponse oauthResponse =
                makeTokenRequestWithAuthCode(authCode);
            assertNotNull(oauthResponse.getAccessToken());
            assertNotNull(oauthResponse.getExpiresIn());
        } catch (OAuthSystemException |
                URISyntaxException |
                JSONException |
                OAuthProblemException ex) {
            Logger.getLogger(AuthTest.class.getName())
                .log(Level.SEVERE, null, ex);
        }
    }

    @Test
    public void directTokenRequest() {
        try {
            OAuthClientRequest request = OAuthClientRequest
                    .tokenLocation(url.toString() + "api/token")
                    .setGrantType(GrantType.PASSWORD)
                    .setClientId(Common.CLIENT_ID)
                    .setClientSecret(Common.CLIENT_SECRET)
                    .setUsername(Common.USERNAME)
                    .setPassword(Common.PASSWORD)
                    .buildBodyMessage();

            OAuthClient oAuthClient =
                new OAuthClient(new URLConnectionClient());
            OAuthAccessTokenResponse oauthResponse =
                oAuthClient.accessToken(request);
            assertNotNull(oauthResponse.getAccessToken());
            assertNotNull(oauthResponse.getExpiresIn());
        } catch (OAuthSystemException |
                OAuthProblemException ex ) {
            Logger.getLogger(AuthTest.class.getName())
                .log(Level.SEVERE, null, ex);
        }
    }

    @Test
    public void endToEndWithAuthCode() {
        try {
            Response response = makeAuthCodeRequest();
            Assert.assertEquals(Status.OK.getStatusCode(),
                response.getStatus());

            String authCode = getAuthCode(response);
            Assert.assertNotNull(authCode);

            OAuthAccessTokenResponse oauthResponse =
                makeTokenRequestWithAuthCode(authCode);
            String accessToken = oauthResponse.getAccessToken();

            URL restUrl = new URL(url.toString() + "api/resource");
            WebTarget target = client.target(restUrl.toURI());
            String entity = target.request(MediaType.TEXT_HTML)
                    .header(Common.HEADER_AUTHORIZATION, "Bearer " +
                        accessToken)
                    .get(String.class);
            System.out.println("Response = " + entity);
        } catch (MalformedURLException |
                URISyntaxException |
                OAuthProblemException |
                OAuthSystemException |
                JSONException ex) {
            Logger.getLogger(AuthTest.class.getName())
                .log(Level.SEVERE, null, ex);
        }
    }

    void testValidTokenResponse(HttpURLConnection httpURLConnection)
            throws Exception {
        InputStream inputStream;
        if (httpURLConnection.getResponseCode() == 400) {
            inputStream = httpURLConnection.getErrorStream();
        } else {
            inputStream = httpURLConnection.getInputStream();
        }
        String responseBody = OAuthUtils.saveStreamAsString(inputStream);
        assert (Common.ACCESS_TOKEN_VALID.equals(responseBody));
    }

    private Response makeAuthCodeRequest() throws OAuthSystemException,
            URISyntaxException {
        OAuthClientRequest request = OAuthClientRequest
                .authorizationLocation(url.toString() + "api/authz")
                .setClientId(Common.CLIENT_ID)
                .setRedirectURI(url.toString() + "api/redirect")
                .setResponseType(ResponseType.CODE.toString())
                .setState("state")
                .buildQueryMessage();
        WebTarget target = client.target(new URI(request.getLocationUri()));
        Response response = target.request(MediaType.TEXT_HTML).get();
        return response;
    }

    private String getAuthCode(Response response) throws JSONException {
        JSONObject obj = new JSONObject(response.readEntity(String.class));
        JSONObject qp = obj.getJSONObject("queryParameters");
        String authCode = null;
        if (qp != null) {
            authCode = qp.getString("code");
        }

        return authCode;
    }

    private OAuthAccessTokenResponse
            makeTokenRequestWithAuthCode(String authCode)
        throws OAuthProblemException, OAuthSystemException {
        OAuthClientRequest request = OAuthClientRequest
                .tokenLocation(url.toString() + "api/token")
                .setClientId(Common.CLIENT_ID)
                .setClientSecret(Common.CLIENT_SECRET)
                .setGrantType(GrantType.AUTHORIZATION_CODE)
                .setCode(authCode)
                .setRedirectURI(url.toString() + "api/redirect")
                .buildBodyMessage();
        OAuthClient oAuthClient =
            new OAuthClient(new URLConnectionClient());
        OAuthAccessTokenResponse oauthResponse =
            oAuthClient.accessToken(request);
        return oauthResponse;
    }
}

The first thing you should notice is that we’re using TestNG and Arquillian. I won’t go into the details on the Arquillian set up here, other than to note that we need our test to @RunAsClient, and to point out the @Deployment method that builds our test archive for us.

Moving on to authorizationRequest, we can see (in makeAuthCodeRequest) how the Oltu library makes it easy to build the request for an authorization code. Utlimately, the library helps use create the request URI, which we then pass to the JAX-RS client as it makes the actual request. To be honest, there’s a bit here (such as the state field) that I don’t understand. Any expert help here would be appreciated. :)

The next method, authCodeTokenRequest, shows the flow of getting an authorization code, then using it to get the access token. That’s followed by an example of a direct request for token via the password grant type. Finally, we have an end to end example, from authorization code to accessing our protected resource.

That’s all there is to it. As you can see in the POM and arquillian.xml, the only container currently supported is GlassFish, which the tests expect to find in glassfish4/ in the project’s base directory. Once that’s installed, the tests can be run with the normal mvn test.

If you have any questions about the code, I can try to answer them, but as should be clear by now, I’m still learning all of this. If I’ve made any mistakes in the code or my description of the protocol, please don’t be shy about correcting me. We’re all hear to learn. :)

Quotes

Sample quote

Quote source