Implementation of OAtuth pages
This commit is contained in:
parent
fee8180690
commit
8af91a9169
3 changed files with 579 additions and 0 deletions
198
src/zutil/net/http/page/oauth/OAuth2AuthorizationPage.java
Normal file
198
src/zutil/net/http/page/oauth/OAuth2AuthorizationPage.java
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Ziver Koc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Ziver Koc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package zutil.net.http.page.oauth;
|
||||
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.net.http.HttpHeader;
|
||||
import zutil.net.http.HttpPage;
|
||||
import zutil.net.http.HttpPrintStream;
|
||||
import zutil.net.http.HttpURL;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* This endpoint is the first step in the OAuth 2 procedure.
|
||||
* The purpose of this page is get authorization from the user to share a resource.
|
||||
*
|
||||
* <pre>
|
||||
* From RFC:
|
||||
* +---------+ +---------------+
|
||||
* | |>---(D)-- Authorization Code ---->| |
|
||||
* | Client | and Redirection URI | Authorization |
|
||||
* | | | Server |
|
||||
* | |<---(E)----- Access Token -------<| |
|
||||
* +---------+ (w/ Optional Refresh Token) +---------------+
|
||||
* </pre>
|
||||
*
|
||||
* @see <a href="https://tools.ietf.org/html/rfc6749#section-4">RFC 6749: Chapter 4</a>
|
||||
*/
|
||||
public class OAuth2AuthorizationPage implements HttpPage {
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
|
||||
/** The request is missing a required parameter, includes an invalid parameter value, includes a parameter
|
||||
more than once, or is otherwise malformed. **/
|
||||
protected static final String ERROR_INVALID_REQUEST = "invalid_request";
|
||||
/** The client is not authorized to request an authorization code using this method. **/
|
||||
protected static final String ERROR_UNAUTHORIZED_CLIENT = "unauthorized_client";
|
||||
/** The resource owner or authorization server denied the request. **/
|
||||
protected static final String ERROR_ACCESS_DENIED = "access_denied";
|
||||
/** The authorization server does not support obtaining an authorization code using this method. **/
|
||||
protected static final String ERROR_UNSUPPORTED_RESP_TYPE = "unsupported_response_type";
|
||||
/** The requested scope is invalid, unknown, or malformed. **/
|
||||
protected static final String ERROR_INVALID_SCOPE = "invalid_scope";
|
||||
/** The authorization server encountered an unexpected condition that prevented it from fulfilling the request.
|
||||
(This error code is needed because a 500 Internal Server Error HTTP status code cannot be returned to the client
|
||||
via an HTTP redirect.) **/
|
||||
protected static final String ERROR_SERVER_ERROR = "server_error";
|
||||
/** The authorization server is currently unable to handle the request due to a temporary overloading or maintenance
|
||||
of the server. (This error code is needed because a 503 Service Unavailable HTTP status code cannot be returned
|
||||
to the client via an HTTP redirect.) **/
|
||||
protected static final String ERROR_TEMPORARILY_UNAVAILABLE = "temporarily_unavailable";
|
||||
|
||||
private static final String RESPONSE_TYPE_CODE = "code";
|
||||
private static final String RESPONSE_TYPE_PASSWORD = "password";
|
||||
private static final String RESPONSE_TYPE_CREDENTIALS = "client_credentials";
|
||||
|
||||
private Random random = new Random();
|
||||
private OAuth2Registry registry;
|
||||
|
||||
|
||||
public OAuth2AuthorizationPage(OAuth2Registry registry) {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void respond(
|
||||
HttpPrintStream out,
|
||||
HttpHeader headers,
|
||||
Map<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> request) throws MalformedURLException {
|
||||
|
||||
if (!request.containsKey("redirect_uri")) {
|
||||
errorResponse(out, "Bad Request, missing property: redirect_uri");
|
||||
return;
|
||||
}
|
||||
|
||||
String clientId = request.get("client_id");
|
||||
|
||||
if (registry.isClientIdValid(clientId)) {
|
||||
errorResponse(out, "Bad Request, missing or invalid client_id value.");
|
||||
return;
|
||||
}
|
||||
|
||||
HttpURL url = new HttpURL(URLDecoder.decode(request.get("redirect_uri")));
|
||||
|
||||
if (!"HTTPS".equalsIgnoreCase(url.getProtocol())) {
|
||||
errorResponse(out, "Bad redirect protocol: " + url.getProtocol());
|
||||
return;
|
||||
}
|
||||
|
||||
switch (request.get("response_type")) {
|
||||
case RESPONSE_TYPE_CODE:
|
||||
String code = generateCode();
|
||||
registry.registerAuthorizationCode(clientId, code);
|
||||
|
||||
url.setParameter("state", request.get("state"));
|
||||
url.setParameter("code", code);
|
||||
break;
|
||||
case RESPONSE_TYPE_PASSWORD:
|
||||
case RESPONSE_TYPE_CREDENTIALS:
|
||||
default:
|
||||
errorRedirect(out, url, ERROR_INVALID_REQUEST, request.get("state"),
|
||||
"unsupported response_type: " + request.get("response_type"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup the redirect
|
||||
|
||||
redirect(out, url);
|
||||
}
|
||||
|
||||
private String generateCode() {
|
||||
return String.valueOf(random.nextInt());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------
|
||||
// Error handling
|
||||
// ------------------------------------------------------
|
||||
|
||||
private static void errorResponse(HttpPrintStream out, String description) {
|
||||
out.setResponseStatusCode(400);
|
||||
out.println(description);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see <a href="https://tools.ietf.org/html/rfc6749#section-4.2.2.1">RFC 6749: Chapter 4.2.2.1</a>
|
||||
*
|
||||
* @param out
|
||||
* @param url
|
||||
* @param error
|
||||
* @param state
|
||||
* @param description
|
||||
*/
|
||||
private static void errorRedirect(HttpPrintStream out, HttpURL url, String error, String state, String description) {
|
||||
out.setHeader(HttpHeader.HEADER_CONTENT_TYPE, "application/x-www-form-urlencoded");
|
||||
url.setParameter("error", error);
|
||||
if (description != null) url.setParameter("error_description", description);
|
||||
//if (uri != null) url.setParameter("error_uri", uri);
|
||||
if (state != null) url.setParameter("state", state);
|
||||
|
||||
redirect(out, url);
|
||||
}
|
||||
|
||||
private static void redirect(HttpPrintStream out, HttpURL url) {
|
||||
out.setResponseStatusCode(302);
|
||||
out.setHeader(HttpHeader.HEADER_LOCATION, url.toString());
|
||||
}
|
||||
}
|
||||
196
src/zutil/net/http/page/oauth/OAuth2Registry.java
Normal file
196
src/zutil/net/http/page/oauth/OAuth2Registry.java
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Ziver Koc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Ziver Koc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package zutil.net.http.page.oauth;
|
||||
|
||||
import zutil.Timer;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A data class containing authentication information for individual
|
||||
* clients going through the OAuth 2 process.
|
||||
*/
|
||||
public class OAuth2Registry {
|
||||
private static final long DEFAULT_TIMEOUT = 24 * 60 * 60 * 1000; // 24h
|
||||
|
||||
private Map<String, ClientRegister> clientRegistry = new HashMap<>();
|
||||
private boolean requireWhitelist = false;
|
||||
|
||||
|
||||
// ------------------------------------------------------
|
||||
// Whitelist methods
|
||||
// ------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Set the requirement or non-requirement of pre-registered client-ids.
|
||||
* If enabled then any clients starting a OAuth2 process needs to have a
|
||||
* preregistered client-id value in the registry object.
|
||||
*
|
||||
* @param enabled if true then all requests will be required to be in whitelist
|
||||
*/
|
||||
public void requireWhitelisting(boolean enabled) {
|
||||
requireWhitelist = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a client-id to be whitelisted. Note this function
|
||||
* has no impact if requireWhitelisting is set to false.
|
||||
*
|
||||
* @param clientId A String ID that should be whitelisted
|
||||
*/
|
||||
public void addWhitelist(String clientId) {
|
||||
if (!clientRegistry.containsKey(clientId)) {
|
||||
clientRegistry.put(clientId, new ClientRegister());
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------
|
||||
// Validation methods
|
||||
// ------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Validates a client_id value is a valid value and is in the whitelist fro approved clients.
|
||||
*
|
||||
* @param clientId the client_id value to validate
|
||||
* @return true if the client_id is allowed to start the OAuth process.
|
||||
*/
|
||||
public boolean isClientIdValid(String clientId) {
|
||||
if (clientId == null)
|
||||
return false;
|
||||
|
||||
if (!requireWhitelist)
|
||||
return true;
|
||||
return clientRegistry.containsKey(clientId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that a authorization code has valid format and has been authorized and not elapsed.
|
||||
*
|
||||
* @param clientId the id of the requesting client
|
||||
* @param code the code that should be validated
|
||||
* @return true if the given code is valid otherwise false.
|
||||
*/
|
||||
public boolean isAuthorizationCodeValid(String clientId, String code) {
|
||||
if (clientId == null || code == null)
|
||||
return false;
|
||||
|
||||
ClientRegister reg = getClientRegistry(clientId);
|
||||
|
||||
if (reg != null) {
|
||||
return reg.authCodes.containsKey(code) &&
|
||||
!reg.authCodes.get(code).hasTimedOut();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that a access token has valid format and has been authorized and not elapsed.
|
||||
*
|
||||
* @param clientId the id of the requesting client
|
||||
* @param token the token that should be validated
|
||||
* @return true if the given token is valid otherwise false.
|
||||
*/
|
||||
public boolean isAccessTokenValid(String clientId, String token) {
|
||||
if (clientId == null || token == null)
|
||||
return false;
|
||||
|
||||
ClientRegister reg = getClientRegistry(clientId);
|
||||
|
||||
if (reg != null) {
|
||||
return reg.accessTokens.containsKey(token) &&
|
||||
!reg.accessTokens.get(token).hasTimedOut();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------
|
||||
// OAuth2 process methods
|
||||
// ------------------------------------------------------
|
||||
|
||||
protected long registerAuthorizationCode(String clientId, String code) {
|
||||
return registerAuthorizationCode(clientId, code, DEFAULT_TIMEOUT);
|
||||
}
|
||||
protected long registerAuthorizationCode(String clientId, String code, long timeoutMillis) {
|
||||
ClientRegister reg = getClientRegistry(clientId);
|
||||
|
||||
if (reg != null) {
|
||||
reg.authCodes.put(code, new Timer(timeoutMillis));
|
||||
return timeoutMillis;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
protected long registerAccessToken(String clientId, String token) {
|
||||
return registerAccessToken(clientId, token, DEFAULT_TIMEOUT);
|
||||
}
|
||||
protected long registerAccessToken(String clientId, String token, long timeoutMillis) {
|
||||
ClientRegister reg = getClientRegistry(clientId);
|
||||
|
||||
if (reg != null) {
|
||||
reg.accessTokens.put(token, new Timer(timeoutMillis));
|
||||
return timeoutMillis;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
private ClientRegister getClientRegistry(String clientId) {
|
||||
if (!requireWhitelist && !clientRegistry.containsKey(clientId))
|
||||
clientRegistry.put(clientId, new ClientRegister());
|
||||
|
||||
return clientRegistry.get(clientId);
|
||||
}
|
||||
|
||||
private static class ClientRegister {
|
||||
Map<String, Timer> authCodes = new HashMap<>();
|
||||
Map<String, Timer> accessTokens = new HashMap<>();
|
||||
}
|
||||
}
|
||||
185
src/zutil/net/http/page/oauth/OAuth2TokenPage.java
Normal file
185
src/zutil/net/http/page/oauth/OAuth2TokenPage.java
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Ziver Koc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Ziver Koc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package zutil.net.http.page.oauth;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.net.http.HttpHeader;
|
||||
import zutil.net.http.HttpPrintStream;
|
||||
import zutil.net.http.page.HttpJsonPage;
|
||||
import zutil.parser.DataNode;
|
||||
|
||||
/**
|
||||
* This endpoint is the second step in the OAuth 2 procedure.
|
||||
* The purpose of this page is give a token that should be used for all consequent HTTP.
|
||||
*
|
||||
* <pre>
|
||||
* From RFC:
|
||||
* +---------+ +---------------+
|
||||
* | | | |
|
||||
* | |>--(A)- Client Authentication --->| Authorization |
|
||||
* | Client | | Server |
|
||||
* | |<--(B)---- Access Token ---------<| |
|
||||
* | | | |
|
||||
* +---------+ +---------------+
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* @see <a href="https://tools.ietf.org/html/rfc6749#section-5">RFC 6749: Chapter 5</a>
|
||||
*/
|
||||
public class OAuth2TokenPage extends HttpJsonPage {
|
||||
private static final Logger logger = LogUtil.getLogger();
|
||||
|
||||
/** The request is missing a required parameter, includes an unsupported parameter value (other than grant type),
|
||||
repeats a parameter, includes multiple credentials, utilizes more than one mechanism for authenticating the
|
||||
client, or is otherwise malformed. **/
|
||||
protected static final String ERROR_INVALID_REQUEST = "invalid_request";
|
||||
/** Client authentication failed (e.g., unknown client, no client authentication included, or unsupported
|
||||
authentication method). **/
|
||||
protected static final String ERROR_INVALID_CLIENT = "invalid_client";
|
||||
/** The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is
|
||||
invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to
|
||||
another client. **/
|
||||
protected static final String ERROR_INVALID_GRANT = "invalid_grant";
|
||||
/** The authenticated client is not authorized to use this authorization grant type. **/
|
||||
protected static final String ERROR_UNAUTHORIZED_CLIENT = "unauthorized_client";
|
||||
/** The authorization grant type is not supported by the authorization server. **/
|
||||
protected static final String ERROR_UNSUPPORTED_GRANT_TYPE = "unsupported_grant_type";
|
||||
/** The requested scope is invalid, unknown, malformed, or exceeds the scope granted by the resource owner. **/
|
||||
protected static final String ERROR_INVALID_SCOPE = "invalid_scope";
|
||||
|
||||
private Random random = new Random();
|
||||
private OAuth2Registry registry;
|
||||
|
||||
|
||||
public OAuth2TokenPage(OAuth2Registry registry) {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public DataNode jsonRespond(
|
||||
HttpPrintStream out,
|
||||
HttpHeader headers,
|
||||
Map<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> request) {
|
||||
|
||||
// POST
|
||||
|
||||
out.setHeader("Access-Control-Allow-Origin", "*");
|
||||
out.setHeader("Cache-Control", "no-store");
|
||||
out.setHeader("Pragma", "no-cache");
|
||||
|
||||
DataNode jsonRes = new DataNode(DataNode.DataType.Map);
|
||||
|
||||
if (!request.containsKey("client_id"))
|
||||
return errorResponse(out, ERROR_INVALID_REQUEST , request.get("state"), "Missing mandatory parameter client_id.");
|
||||
|
||||
String clientId = request.get("client_id");
|
||||
|
||||
if (registry.isClientIdValid(clientId))
|
||||
return errorResponse(out, ERROR_INVALID_CLIENT , request.get("state"), "Invalid client_id value.");
|
||||
|
||||
if (!registry.isAuthorizationCodeValid(clientId, request.get("code")))
|
||||
return errorResponse(out, ERROR_INVALID_GRANT, request.get("state"), "Invalid authorization code value provided.");
|
||||
|
||||
String grantType = request.get("grant_type");
|
||||
|
||||
switch (grantType) {
|
||||
case "authorization_code":
|
||||
jsonRes.set("refresh_token", "TODO"); // TODO: implement refresh logic
|
||||
break;
|
||||
default:
|
||||
return errorResponse(out, ERROR_UNSUPPORTED_GRANT_TYPE, request.get("state"), "Unsupported grant_type: " + request.containsKey("grant_type"));
|
||||
}
|
||||
|
||||
String token = generateToken();
|
||||
long timeoutMillis = registry.registerAccessToken(clientId, token);
|
||||
|
||||
jsonRes.set("access_token", token);
|
||||
jsonRes.set("token_type", "bearer");
|
||||
jsonRes.set("expires_in", timeoutMillis/1000);
|
||||
//jsonRes.set("scope", ?);
|
||||
if (request.containsKey("state")) jsonRes.set("state", request.get("state"));
|
||||
|
||||
return jsonRes;
|
||||
}
|
||||
|
||||
private String generateToken() {
|
||||
return String.valueOf(random.nextInt());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------
|
||||
// Error handling
|
||||
// ------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @see <a href="https://tools.ietf.org/html/rfc6749#section-5.2">RFC 6749: Chapter 5.2</a>
|
||||
*
|
||||
* @param out
|
||||
* @param error
|
||||
* @param state
|
||||
* @param description
|
||||
* @return A DataNode containing the error response
|
||||
*/
|
||||
private static DataNode errorResponse(HttpPrintStream out, String error, String state, String description) {
|
||||
out.setResponseStatusCode(400);
|
||||
|
||||
DataNode jsonErr = new DataNode(DataNode.DataType.Map);
|
||||
jsonErr.set("error", error);
|
||||
if (description != null) jsonErr.set("error_description", description);
|
||||
//if (uri != null) jsonErr.set("error_uri", uri);
|
||||
if (state != null) jsonErr.set("state", state);
|
||||
|
||||
return jsonErr;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue