com.soundcloud.api.OAuth2Scheme Maven / Gradle / Ivy
package com.soundcloud.api;
import org.apache.http.FormattedHeader;
import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HttpRequest;
import org.apache.http.auth.AUTH;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthSchemeFactory;
import org.apache.http.auth.AuthenticationException;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.MalformedChallengeException;
import org.apache.http.message.BasicHeaderValueParser;
import org.apache.http.message.HeaderValueParser;
import org.apache.http.message.ParserCursor;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.CharArrayBuffer;
import java.io.IOException;
import java.security.Principal;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
class OAuth2Scheme implements AuthScheme {
public HashMap mParams;
public HttpParams mHttpParams;
private CloudAPI mApi;
public static Pattern AUTHORIZATION_HEADER_PATTERN = Pattern.compile("^OAuth (\\w+)$");
public OAuth2Scheme(CloudAPI api, HttpParams params) {
mApi = api;
mHttpParams = params;
mParams = new HashMap();
}
@Override public String getSchemeName() {
return CloudAPI.OAUTH_SCHEME;
}
@Override public String getParameter(String name) {
return mParams.get(name);
}
@Override public String getRealm() {
return getParameter("realm");
}
@Override public boolean isConnectionBased() {
return false;
}
@Override public boolean isComplete() {
return true;
}
@Override public Header authenticate(Credentials credentials, HttpRequest request)
throws AuthenticationException {
final String usedToken = extractToken(request);
// make sure only one refresh request gets sent out
synchronized (OAuth2Scheme.class) {
final Token apiToken = mApi.getToken();
if (apiToken == null || apiToken.access == null || apiToken.access.equals(usedToken)) {
if (mApi.invalidateToken() == null) {
// we actually need to refresh it ourselves
try {
mApi.refreshToken();
} catch (IOException e) {
throw new AuthenticationException("Error refreshing token", e);
} catch (IllegalStateException e) {
throw new AuthenticationException("Error refreshing token", e);
}
}
}
return ApiWrapper.createOAuthHeader(mApi.getToken());
}
}
@Override public void processChallenge(Header header) throws MalformedChallengeException {
if (header == null) {
throw new IllegalArgumentException("Header may not be null");
}
String authHeader = header.getName();
if (!authHeader.equalsIgnoreCase(AUTH.WWW_AUTH)) {
throw new MalformedChallengeException("Unexpected header name: " + authHeader);
}
CharArrayBuffer buffer;
int pos;
if (header instanceof FormattedHeader) {
buffer = ((FormattedHeader) header).getBuffer();
pos = ((FormattedHeader) header).getValuePos();
} else {
String s = header.getValue();
if (s == null) {
throw new MalformedChallengeException("Header value is null");
}
buffer = new CharArrayBuffer(s.length());
buffer.append(s);
pos = 0;
}
while (pos < buffer.length() && HTTP.isWhitespace(buffer.charAt(pos))) {
pos++;
}
int beginIndex = pos;
while (pos < buffer.length() && !HTTP.isWhitespace(buffer.charAt(pos))) {
pos++;
}
int endIndex = pos;
String s = buffer.substring(beginIndex, endIndex);
if (!s.equalsIgnoreCase(getSchemeName())) {
throw new MalformedChallengeException("Invalid scheme identifier: " + s);
}
HeaderValueParser parser = BasicHeaderValueParser.DEFAULT;
ParserCursor cursor = new ParserCursor(pos, buffer.length());
HeaderElement[] elements = parser.parseElements(buffer, cursor);
if (elements.length == 0) {
throw new MalformedChallengeException("Authentication challenge is empty");
}
for (HeaderElement element : elements) {
this.mParams.put(element.getName(), element.getValue());
}
}
static String extractToken(HttpRequest r) {
return (r == null) ? null : extractToken(r.getFirstHeader(AUTH.WWW_AUTH_RESP));
}
static String extractToken(Header h) {
if (h ==null || h.getValue() == null) return null;
if (AUTH.WWW_AUTH_RESP.equalsIgnoreCase(h.getName())) {
Matcher m = AUTHORIZATION_HEADER_PATTERN.matcher(h.getValue());
return m.matches() ? m.group(1) : null;
} else {
return null;
}
}
static class Factory implements AuthSchemeFactory {
private CloudAPI api;
public Factory(CloudAPI api) {
this.api = api;
}
@Override
public AuthScheme newInstance(HttpParams params) {
return new OAuth2Scheme(api, params);
}
}
static class EmptyCredentials implements Credentials {
public static final Credentials INSTANCE = new EmptyCredentials();
@Override public Principal getUserPrincipal() {
return null;
}
@Override public String getPassword() {
return null;
}
}
}