Step 4b. Implement Android bridge code
In this step you implement the main Android bridging code that provides the callable methods for the Flutter layer.
-
In Android Studio, open
sdk-sample-apps/flutter/flutter_todo, and navigate to theandroid/app/src/main/java/com/example/flutter_todo/FRAuthSampleBridge.javafile. -
Update the
Configurationclass with the details of your server, and the authentication journey and public OAuth 2.0 client you created earlier.
A hypothetical example (your values may vary):
FRAuthSampleBridge.javaclass Configuration {
/ The main authentication journey name. */
static String mainAuthenticationJourney = "sdkUsernamePasswordJourney";
/ The URL of the authentication server. */
static String amURL = "https://openam-forgerock-sdks.forgeblocks.com/am";
/ The name of the cookie used for authentication. */
static String cookieName = "ch15fefc5407912";
/ The realm used for authentication. */
static String realm = "alpha";
/ The OAuth client ID. */
static String oauthClientId = "sdkPublicClient";
/ The OAuth redirect URI. */
static String oauthRedirectURI = "https://com.example.flutter.todo/callback";
/ The OAuth scopes. */
static String oauthScopes = "openid profile email address";
/ The discovery endpoint for OAuth configuration. */
static String discoveryEndpoint = "https://openam-forgerock-sdks.forgeblocks.com/am/oauth2/realms/root/realms/alpha/.well-known/openid-configuration";
}
Write the start() method
For the SDK to initialize, write the start function as follows:
FRAuthSampleBridge.javapublic void start(MethodChannel.Result promise) {
Logger.set(Logger.Level.DEBUG);
FROptions options = FROptionsBuilder.build(frOptionsBuilder -> {
frOptionsBuilder.server(serverBuilder -> {
serverBuilder.setUrl(Configuration.amURL);
serverBuilder.setRealm(Configuration.realm);
serverBuilder.setCookieName(Configuration.cookieName);
return null;
});
frOptionsBuilder.oauth(oAuthBuilder -> {
oAuthBuilder.setOauthClientId(Configuration.oauthClientId);
oAuthBuilder.setOauthRedirectUri(Configuration.oauthRedirectURI);
oAuthBuilder.setOauthScope(Configuration.oauthScopes);
return null;
});
frOptionsBuilder.service(serviceBuilder -> {
serviceBuilder.setAuthServiceName(Configuration.mainAuthenticationJourney);
serviceBuilder.setRegistrationServiceName("Registration");
return null;
});
return null;
});
FRAuth.start(this.context, options);
promise.success("SDK Initialized");
// Clear the session - for debug reasons
FRUser user = FRUser.getCurrentUser();
if (user != null) {
user.logout();
}
}
The start() function above calls the Ping SDK for Android start() method on the FRAuth class.
Write the login() method
Once the start() method is called, and it has initialized, the SDK is ready to handle user requests.
Let’s start with login().
Just underneath the start() method we wrote above, add the login() method.
FRAuthSampleBridge.javapublic void login(MethodChannel.Result promise) {
try {
authenticate(promise, true);
} catch (Exception e) {
promise.error("error", e.toString(), e);
}
}
This login() function initializes the journey/tree specified for authentication.
You call this method without arguments as it does not log the user in.
This initial call to the server will return the first set of callbacks
that represents the first node in your journey/tree to collect user data.
Write the next() method
To finalize the functionality needed to complete user authentication,
we need a way to iteratively call next() until the authentication journey completes successfully or fails.
FRAuthSampleBridge.javapublic void next(String response, MethodChannel.Result promise) throws InterruptedException {
this.flutterPromise = promise;
Gson gson= new Gson();
Response responseObj = gson.fromJson(response,Response.class);
if (responseObj != null) {
List<Callback> callbacksList = currentNode.getCallbacks();
for(int i = 0; i < callbacksList.size(); i++) {
Object nodeCallback = callbacksList.get(i);
for(int j = 0; j < responseObj.callbacks.size(); j++) {
RawCallback callback = responseObj.callbacks.get(j);
String currentCallbackType = callback.type;
RawInput input = callback.input.get(0);
if ((currentCallbackType.equals("NameCallback")) && i==j) {
currentNode.getCallback(NameCallback.class).setName((String) input.value);
}
if ((currentCallbackType.equals("ValidatedCreateUsernameCallback")) && i==j) {
currentNode.getCallback(ValidatedUsernameCallback.class).setUsername((String) input.value);
}
if ((currentCallbackType.equals("ValidatedCreatePasswordCallback")) && i==j) {
String password = (String) input.value;
currentNode.getCallback(ValidatedPasswordCallback.class).setPassword(password.toCharArray());
}
if ((currentCallbackType.equals("PasswordCallback")) && i==j) {
String password = (String) input.value;
currentNode.getCallback(PasswordCallback.class).setPassword(password.toCharArray());
}
if ((currentCallbackType.equals("ChoiceCallback")) && i==j) {
currentNode.getCallback(ChoiceCallback.class).setSelectedIndex((Integer) input.value);
}
if ((currentCallbackType.equals("KbaCreateCallback")) && i==j) {
for (RawInput rawInput : callback.input) {
if (rawInput.name.contains("question")) {
currentNode.getCallback(KbaCreateCallback.class).setSelectedQuestion((String) rawInput.value);
} else {
currentNode.getCallback(KbaCreateCallback.class).setSelectedAnswer((String) rawInput.value);
}
}
}
if ((currentCallbackType.equals("StringAttributeInputCallback")) && i==j) {
StringAttributeInputCallback stringAttributeInputCallback = (StringAttributeInputCallback) nodeCallback;
stringAttributeInputCallback.setValue((String) input.value);
}
if ((currentCallbackType.equals("BooleanAttributeInputCallback")) && i==j) {
BooleanAttributeInputCallback boolAttributeInputCallback = (BooleanAttributeInputCallback) nodeCallback;
boolAttributeInputCallback.setValue((Boolean) input.value);
}
if ((currentCallbackType.equals("TermsAndConditionsCallback")) && i==j) {
TermsAndConditionsCallback tcAttributeInputCallback = (TermsAndConditionsCallback) nodeCallback;
tcAttributeInputCallback.setAccept((Boolean) input.value);
}
if (currentCallbackType.equals("DeviceProfileCallback") && i==j) {
final Semaphore available = new Semaphore(1, true);
available.acquire();
currentNode.getCallback(DeviceProfileCallback.class).execute(context, new FRListener<Void>() {
@Override
public void onSuccess(Void result) {
Logger.warn("DeviceProfileCallback", "Device Profile Collection Succeeded");
available.release();
}
@Override
public void onException(Exception e) {
Logger.warn("DeviceProfileCallback", e, "Device Profile Collection Failed");
available.release();
}
});
}
}
}
} else {
promise.error("error", "parsing response failed", null);
}
currentNode.next(this.context, listener);
}
The above code handles a limited number of callback types. Handling full authentication and registration journeys/trees requires additional callback handling.
To keep this tutorial simple, we’ll focus just on SingleValueCallback type.