/
AbstractAuthorizationCodeCallbackServlet.java
189 lines (177 loc) · 7.09 KB
/
AbstractAuthorizationCodeCallbackServlet.java
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
179
180
181
182
183
184
185
186
187
188
189
/*
* Copyright (c) 2012 Google Inc.
*
* Licensed 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 com.google.api.client.extensions.servlet.auth.oauth2;
import com.google.api.client.auth.oauth2.AuthorizationCodeFlow;
import com.google.api.client.auth.oauth2.AuthorizationCodeResponseUrl;
import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.auth.oauth2.TokenResponse;
import java.io.IOException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Thread-safe OAuth 2.0 authorization code callback servlet to process the authorization code or
* error response from authorization page redirect.
*
* <p>This is designed to simplify the flow in which an end-user authorizes your web application to
* access their protected data. The main servlet class extends {@link
* AbstractAuthorizationCodeServlet} which if the end-user credentials are not found, will redirect
* the end-user to an authorization page. If the end-user grants authorization, they will be
* redirected to this servlet that extends {@link AbstractAuthorizationCodeCallbackServlet} and the
* {@link #onSuccess} will be called. Similarly, if the end-user grants authorization, they will be
* redirected to this servlet and {@link #onError} will be called.
*
* <p>Sample usage:
*
* <pre>
* public class ServletCallbackSample extends AbstractAuthorizationCodeCallbackServlet {
*
* @Override
* protected void onSuccess(HttpServletRequest req, HttpServletResponse resp, Credential credential)
* throws ServletException, IOException {
* resp.sendRedirect("/");
* }
*
* @Override
* protected void onError(
* HttpServletRequest req, HttpServletResponse resp, AuthorizationCodeResponseUrl errorResponse)
* throws ServletException, IOException {
* // handle error
* }
*
* @Override
* protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException {
* GenericUrl url = new GenericUrl(req.getRequestURL().toString());
* url.setRawPath("/oauth2callback");
* return url.build();
* }
*
* @Override
* protected AuthorizationCodeFlow initializeFlow() throws IOException {
* return new AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(),
* new NetHttpTransport(),
* new GsonFactory(),
* new GenericUrl("https://server.example.com/token"),
* new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"),
* "s6BhdRkqt3",
* "https://server.example.com/authorize").setCredentialStore(
* new JdoCredentialStore(JDOHelper.getPersistenceManagerFactory("transactions-optional")))
* .build();
* }
*
* @Override
* protected String getUserId(HttpServletRequest req) throws ServletException, IOException {
* // return user ID
* }
* }
* </pre>
*
* @since 1.7
* @author Yaniv Inbar
*/
public abstract class AbstractAuthorizationCodeCallbackServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/** Lock on the flow. */
private final Lock lock = new ReentrantLock();
/**
* Authorization code flow to be used across all HTTP servlet requests or {@code null} before
* initialized in {@link #initializeFlow()}.
*/
private AuthorizationCodeFlow flow;
@Override
protected final void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
StringBuffer buf = req.getRequestURL();
if (req.getQueryString() != null) {
buf.append('?').append(req.getQueryString());
}
AuthorizationCodeResponseUrl responseUrl = new AuthorizationCodeResponseUrl(buf.toString());
String code = responseUrl.getCode();
if (responseUrl.getError() != null) {
onError(req, resp, responseUrl);
} else if (code == null) {
resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
resp.getWriter().print("Missing authorization code");
} else {
lock.lock();
try {
if (flow == null) {
flow = initializeFlow();
}
String redirectUri = getRedirectUri(req);
TokenResponse response = flow.newTokenRequest(code).setRedirectUri(redirectUri).execute();
String userId = getUserId(req);
Credential credential = flow.createAndStoreCredential(response, userId);
onSuccess(req, resp, credential);
} finally {
lock.unlock();
}
}
}
/**
* Loads the authorization code flow to be used across all HTTP servlet requests (only called
* during the first HTTP servlet request with an authorization code).
*/
protected abstract AuthorizationCodeFlow initializeFlow() throws ServletException, IOException;
/** Returns the redirect URI for the given HTTP servlet request. */
protected abstract String getRedirectUri(HttpServletRequest req)
throws ServletException, IOException;
/**
* Returns the user ID for the given HTTP servlet request. This identifies your application's user
* and is used to assign and persist credentials to that user. Most commonly, this will be a user
* id stored in the session or even the session id itself.
*/
protected abstract String getUserId(HttpServletRequest req) throws ServletException, IOException;
/**
* Handles a successfully granted authorization.
*
* <p>Default implementation is to do nothing, but subclasses should override and implement.
* Sample implementation:
*
* <pre>
* resp.sendRedirect("/granted");
* </pre>
*
* @param req HTTP servlet request
* @param resp HTTP servlet response
* @param credential credential
* @throws ServletException HTTP servlet exception
* @throws IOException some I/O exception
*/
protected void onSuccess(HttpServletRequest req, HttpServletResponse resp, Credential credential)
throws ServletException, IOException {}
/**
* Handles an error to the authorization, such as when an end user denies authorization.
*
* <p>Default implementation is to do nothing, but subclasses should override and implement.
* Sample implementation:
*
* <pre>
* resp.sendRedirect("/denied");
* </pre>
*
* @param req HTTP servlet request
* @param resp HTTP servlet response
* @param errorResponse error response ({@link AuthorizationCodeResponseUrl#getError()} is not
* {@code null})
* @throws ServletException HTTP servlet exception
* @throws IOException some I/O exception
*/
protected void onError(
HttpServletRequest req, HttpServletResponse resp, AuthorizationCodeResponseUrl errorResponse)
throws ServletException, IOException {}
}