Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package com.getcapacitor.community.genericoauth2;

import com.getcapacitor.JSObject;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
Expand Down Expand Up @@ -185,6 +189,17 @@ public static String getRandomString(int len) {
return new String(c);
}

public static String derivePkceCodeChallenge(String codeVerifier) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] digest = md.digest(codeVerifier.getBytes(StandardCharsets.US_ASCII));
return Base64.getUrlEncoder().withoutPadding().encodeToString(digest);
} catch (NoSuchAlgorithmException e) {
// Fallback to plain if SHA-256 is not available
return codeVerifier;
}
}

public static String trimToNull(String value) {
if (value != null && value.trim().length() == 0) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public class GenericOAuth2Plugin extends Plugin {
private static final String PARAM_STATE = "state";

private static final String PARAM_ACCESS_TOKEN_ENDPOINT = "accessTokenEndpoint";
private static final String PARAM_PAR_ENDPOINT = "parEndpoint";
private static final String PARAM_PKCE_ENABLED = "pkceEnabled";
private static final String PARAM_RESOURCE_URL = "resourceUrl";
private static final String PARAM_ADDITIONAL_RESOURCE_HEADERS = "additionalResourceHeaders";
Expand Down Expand Up @@ -213,75 +214,97 @@ public void onError(Exception error) {
return;
}

// ### Configure

Uri authorizationUri = Uri.parse(oauth2Options.getAuthorizationBaseUrl());
Uri accessTokenUri;
if (oauth2Options.getAccessTokenEndpoint() != null) {
accessTokenUri = Uri.parse(oauth2Options.getAccessTokenEndpoint());
if (oauth2Options.getParEndpoint() != null) {
AsyncTask<Void, Void, ParRequestResult> asyncTask = new ParRequestAsyncTask(call, oauth2Options, getLogTag(), this);
asyncTask.execute();
} else {
// appAuth does not allow to be the accessTokenUri empty although it is not used unit performTokenRequest
accessTokenUri = authorizationUri;
startAuthorization(call);
}
}
}

AuthorizationServiceConfiguration config = new AuthorizationServiceConfiguration(authorizationUri, accessTokenUri);
void startAuthorization(final PluginCall call) {
// ### Configure

if (this.authState == null) {
this.authState = new AuthState(config);
}
Uri authorizationUri = Uri.parse(oauth2Options.getAuthorizationBaseUrl());
Uri accessTokenUri;
if (oauth2Options.getAccessTokenEndpoint() != null) {
accessTokenUri = Uri.parse(oauth2Options.getAccessTokenEndpoint());
} else {
// appAuth does not allow to be the accessTokenUri empty although it is not used until performTokenRequest
accessTokenUri = authorizationUri;
}

AuthorizationRequest.Builder builder = new AuthorizationRequest.Builder(
config,
oauth2Options.getAppId(),
oauth2Options.getResponseType(),
Uri.parse(oauth2Options.getRedirectUrl())
);
AuthorizationServiceConfiguration config = new AuthorizationServiceConfiguration(authorizationUri, accessTokenUri);

// app auth always uses a state
if (oauth2Options.getState() != null) {
builder.setState(oauth2Options.getState());
}
builder.setScope(oauth2Options.getScope());
if (oauth2Options.isPkceEnabled()) {
builder.setCodeVerifier(oauth2Options.getPkceCodeVerifier());
} else {
builder.setCodeVerifier(null);
}
if (oauth2Options.getPrompt() != null) {
builder.setPrompt(oauth2Options.getPrompt());
}
if (oauth2Options.getLoginHint() != null) {
builder.setLoginHint(oauth2Options.getLoginHint());
}
if (oauth2Options.getResponseMode() != null) {
builder.setResponseMode(oauth2Options.getResponseMode());
}
if (oauth2Options.getDisplay() != null) {
builder.setDisplay(oauth2Options.getDisplay());
}
if (this.authState == null) {
this.authState = new AuthState(config);
}

if (oauth2Options.getAdditionalParameters() != null) {
try {
builder.setAdditionalParameters(oauth2Options.getAdditionalParameters());
} catch (IllegalArgumentException e) {
// ignore all additional parameter on error
Log.e(getLogTag(), "Additional parameter error", e);
}
}
AuthorizationRequest.Builder builder = new AuthorizationRequest.Builder(
config,
oauth2Options.getAppId(),
oauth2Options.getResponseType(),
Uri.parse(oauth2Options.getRedirectUrl())
);

AuthorizationRequest req = builder.build();
// app auth always uses a state
if (oauth2Options.getState() != null) {
builder.setState(oauth2Options.getState());
}
builder.setScope(oauth2Options.getScope());
if (oauth2Options.isPkceEnabled()) {
builder.setCodeVerifier(oauth2Options.getPkceCodeVerifier());
} else {
builder.setCodeVerifier(null);
}
if (oauth2Options.getPrompt() != null) {
builder.setPrompt(oauth2Options.getPrompt());
}
if (oauth2Options.getLoginHint() != null) {
builder.setLoginHint(oauth2Options.getLoginHint());
}
if (oauth2Options.getResponseMode() != null) {
builder.setResponseMode(oauth2Options.getResponseMode());
}
if (oauth2Options.getDisplay() != null) {
builder.setDisplay(oauth2Options.getDisplay());
}

this.authService = new AuthorizationService(getContext());
if (oauth2Options.getAdditionalParameters() != null) {
try {
Intent authIntent = this.authService.getAuthorizationRequestIntent(req);
this.bridge.saveCall(call);
startActivityForResult(call, authIntent, "handleIntentResult");
} catch (ActivityNotFoundException e) {
call.reject(ERR_ANDROID_NO_BROWSER, e);
} catch (Exception e) {
Log.e(getLogTag(), "Unexpected exception on open browser for authorization request!");
call.reject(ERR_GENERAL, e);
Map<String, String> additional = oauth2Options.getAdditionalParameters();
if (oauth2Options.getParRequestUri() != null) {
if (additional == null) {
additional = new java.util.HashMap<>();
} else {
additional = new java.util.HashMap<>(additional);
}
additional.put("request_uri", oauth2Options.getParRequestUri());
}
builder.setAdditionalParameters(additional);
} catch (IllegalArgumentException e) {
// ignore all additional parameter on error
Log.e(getLogTag(), "Additional parameter error", e);
}
} else if (oauth2Options.getParRequestUri() != null) {
Map<String, String> additional = new java.util.HashMap<>();
additional.put("request_uri", oauth2Options.getParRequestUri());
builder.setAdditionalParameters(additional);
}

AuthorizationRequest req = builder.build();

this.authService = new AuthorizationService(getContext());
try {
Intent authIntent = this.authService.getAuthorizationRequestIntent(req);
this.bridge.saveCall(call);
startActivityForResult(call, authIntent, "handleIntentResult");
} catch (ActivityNotFoundException e) {
call.reject(ERR_ANDROID_NO_BROWSER, e);
} catch (Exception e) {
Log.e(getLogTag(), "Unexpected exception on open browser for authorization request!");
call.reject(ERR_GENERAL, e);
}
}

Expand Down Expand Up @@ -503,6 +526,7 @@ OAuth2Options buildAuthenticateOptions(JSObject callData) {
Boolean logsEnabled = ConfigUtils.getOverwrittenAndroidParam(Boolean.class, callData, PARAM_LOGS_ENABLED);
o.setLogsEnabled(logsEnabled != null && logsEnabled);
o.setResourceUrl(ConfigUtils.trimToNull(ConfigUtils.getOverwrittenAndroidParam(String.class, callData, PARAM_RESOURCE_URL)));
o.setParEndpoint(ConfigUtils.trimToNull(ConfigUtils.getOverwrittenAndroidParam(String.class, callData, PARAM_PAR_ENDPOINT)));
o.setAccessTokenEndpoint(
ConfigUtils.trimToNull(ConfigUtils.getOverwrittenAndroidParam(String.class, callData, PARAM_ACCESS_TOKEN_ENDPOINT))
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public class OAuth2Options {
private String accessTokenEndpoint;
private String resourceUrl;
private Map<String, String> additionalResourceHeaders;
private String parEndpoint;
private String parRequestUri;

private boolean pkceEnabled;
private boolean logsEnabled;
Expand Down Expand Up @@ -67,6 +69,22 @@ public void setResourceUrl(String resourceUrl) {
this.resourceUrl = resourceUrl;
}

public String getParEndpoint() {
return parEndpoint;
}

public void setParEndpoint(String parEndpoint) {
this.parEndpoint = parEndpoint;
}

public String getParRequestUri() {
return parRequestUri;
}

public void setParRequestUri(String parRequestUri) {
this.parRequestUri = parRequestUri;
}

public boolean isLogsEnabled() {
return logsEnabled;
}
Expand Down
Loading