package com.github.scribejava.core.extractors; import java.io.IOException; import java.net.URI; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.github.scribejava.core.exceptions.OAuthException; import com.github.scribejava.core.model.OAuth2AccessToken; import com.github.scribejava.core.model.OAuth2AccessTokenErrorResponse; import com.github.scribejava.core.model.Response; import com.github.scribejava.core.utils.Preconditions; /** * JSON (default) implementation of {@link TokenExtractor} for OAuth 2.0 */ public class OAuth2AccessTokenJsonExtractor implements TokenExtractor<OAuth2AccessToken> { private static final Pattern ACCESS_TOKEN_REGEX_PATTERN = Pattern.compile("\"access_token\"\\s*:\\s*\"(\\S*?)\""); private static final Pattern TOKEN_TYPE_REGEX_PATTERN = Pattern.compile("\"token_type\"\\s*:\\s*\"(\\S*?)\""); private static final Pattern EXPIRES_IN_REGEX_PATTERN = Pattern.compile("\"expires_in\"\\s*:\\s*\"?(\\d*?)\"?\\D"); private static final Pattern REFRESH_TOKEN_REGEX_PATTERN = Pattern.compile("\"refresh_token\"\\s*:\\s*\"(\\S*?)\""); private static final Pattern SCOPE_REGEX_PATTERN = Pattern.compile("\"scope\"\\s*:\\s*\"(\\S*?)\""); private static final Pattern ERROR_REGEX_PATTERN = Pattern.compile("\"error\"\\s*:\\s*\"(\\S*?)\""); private static final Pattern ERROR_DESCRIPTION_REGEX_PATTERN = Pattern.compile("\"error_description\"\\s*:\\s*\"([^\"]*?)\""); private static final Pattern ERROR_URI_REGEX_PATTERN = Pattern.compile("\"error_uri\"\\s*:\\s*\"(\\S*?)\""); protected OAuth2AccessTokenJsonExtractor() { } private static class InstanceHolder { private static final OAuth2AccessTokenJsonExtractor INSTANCE = new OAuth2AccessTokenJsonExtractor(); } public static OAuth2AccessTokenJsonExtractor instance() { return InstanceHolder.INSTANCE; } @Override public OAuth2AccessToken extract(Response response) throws IOException { final String body = response.getBody(); Preconditions.checkEmptyString(body, "Response body is incorrect. Can't extract a token from an empty string"); if (response.getCode() != 200) { generateError(response.getBody()); } return createToken(body); } /** * Related documentation: https://tools.ietf.org/html/rfc6749#section-5.2 * * @param response response */ protected void generateError(String response) { final String errorInString = extractParameter(response, ERROR_REGEX_PATTERN, true); final String errorDescription = extractParameter(response, ERROR_DESCRIPTION_REGEX_PATTERN, false); final String errorUriInString = extractParameter(response, ERROR_URI_REGEX_PATTERN, false); URI errorUri; try { errorUri = errorUriInString == null ? null : URI.create(errorUriInString); } catch (IllegalArgumentException iae) { errorUri = null; } throw new OAuth2AccessTokenErrorResponse(OAuth2AccessTokenErrorResponse.ErrorCode.valueOf(errorInString), errorDescription, errorUri, response); } private OAuth2AccessToken createToken(String response) { final String accessToken = extractParameter(response, ACCESS_TOKEN_REGEX_PATTERN, true); final String tokenType = extractParameter(response, TOKEN_TYPE_REGEX_PATTERN, false); final String expiresInString = extractParameter(response, EXPIRES_IN_REGEX_PATTERN, false); Integer expiresIn; try { expiresIn = expiresInString == null ? null : Integer.valueOf(expiresInString); } catch (NumberFormatException nfe) { expiresIn = null; } final String refreshToken = extractParameter(response, REFRESH_TOKEN_REGEX_PATTERN, false); final String scope = extractParameter(response, SCOPE_REGEX_PATTERN, false); return createToken(accessToken, tokenType, expiresIn, refreshToken, scope, response); } protected OAuth2AccessToken createToken(String accessToken, String tokenType, Integer expiresIn, String refreshToken, String scope, String response) { return new OAuth2AccessToken(accessToken, tokenType, expiresIn, refreshToken, scope, response); } protected static String extractParameter(String response, Pattern regexPattern, boolean required) throws OAuthException { final Matcher matcher = regexPattern.matcher(response); if (matcher.find()) { return matcher.group(1); } if (required) { throw new OAuthException("Response body is incorrect. Can't extract a '" + regexPattern.pattern() + "' from this: '" + response + "'", null); } return null; } }