ForgeRock SDKs 3.3

Collect the username and password

The authentication journey in this case sends only NameCallback and PasswordCallback challenges.

For demonstration purposes, the sample application adds a simple DialogFragment to collect the username and password. The implementation calls for:

  • A UI layout with the objects to collect the username and password.

  • A NodeDialogFragment class that extends DialogFragment, and processes the Node with the expected callbacks.

  • An update of the MainActivity.onCallbackReceived() method to use the NodeDialogFragment.

Follow these steps:

  1. Implement the UI layout.

    Create a fragment_node.xml layout file, and add fields for the username and password, and buttons to cancel and continue authentication:

    Blueprint of the DialogFragment UI
    • The file to edit is app/src/main/res/layout/fragment_node.xml.

    • The TextInputEditText for the username has ID username.

    • The TextInputEditText for the password has ID password, and has inputType of textPassword.

    • The Cancel button has ID cancel.

    • The Next button has ID next.

  2. Create a NodeDialogFragment class file that extends DialogFragment.

    For example, app/src/main/java/com/example/forgerocksdktutorial/NodeDialogFragment.java:

    public class NodeDialogFragment extends DialogFragment {
    }
  3. Implement a newInstance() method that attaches the Node with the callbacks to the fragment:

    public class NodeDialogFragment extends DialogFragment {
        public static NodeDialogFragment newInstance(Node node) {
            NodeDialogFragment fragment = new NodeDialogFragment();
            Bundle args = new Bundle();
            args.putSerializable("NODE", node);
            fragment.setArguments(args);
            return fragment;
        }
    }
  4. Implement an onCreateView() method that binds the username and password UI items, collects the username and password, and sets the values in the NameCallback and PasswordCallback objects:

    public class NodeDialogFragment extends DialogFragment {
        private MainActivity listener;
        private Node node;
    
        public static NodeDialogFragment newInstance(Node node) {
            // ...
        }
    
        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater,
                                 @Nullable ViewGroup container,
                                 @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.fragment_node, container, false);
            node = (Node) getArguments().getSerializable("NODE");
            TextInputEditText username = view.findViewById(R.id.username);
            TextInputEditText password = view.findViewById(R.id.password);
            Button next = view.findViewById(R.id.next);
            next.setOnClickListener(v -> {
                dismiss();
                node.getCallback(NameCallback.class)
                        .setName(username.getText().toString());
                node.getCallback(PasswordCallback.class)
                        .setPassword(password.getText().toString().toCharArray());
                node.next(getContext(), listener);
            });
            Button cancel = view.findViewById(R.id.cancel);
            cancel.setOnClickListener(v -> {
                dismiss();
            });
            return view;
        }
    }
  5. Implement additional methods to attach the fragment to the main UI.

    Full source code for NodeDialogFragment
    package com.example.forgerocksdktutorial;
    
    import android.content.Context;
    import android.os.Bundle;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.Button;
    
    import androidx.annotation.NonNull;
    import androidx.annotation.Nullable;
    import androidx.fragment.app.DialogFragment;
    
    import com.google.android.material.textfield.TextInputEditText;
    
    import org.forgerock.android.auth.Node;
    import org.forgerock.android.auth.callback.NameCallback;
    import org.forgerock.android.auth.callback.PasswordCallback;
    
    public class NodeDialogFragment extends DialogFragment {
        private MainActivity listener;
        private Node node;
    
        public static NodeDialogFragment newInstance(Node node) {
            NodeDialogFragment fragment = new NodeDialogFragment();
            Bundle args = new Bundle();
            args.putSerializable("NODE", node);
            fragment.setArguments(args);
            return fragment;
        }
    
        @Override
        public void onResume() {
            super.onResume();
            ViewGroup.LayoutParams params = getDialog().getWindow().getAttributes();
            params.width = ViewGroup.LayoutParams.MATCH_PARENT;
            params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
            getDialog().getWindow().setAttributes((android.view.WindowManager.LayoutParams) params);
        }
    
        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater,
                                 @Nullable ViewGroup container,
                                 @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.fragment_node, container, false);
            node = (Node) getArguments().getSerializable("NODE");
            TextInputEditText username = view.findViewById(R.id.username);
            TextInputEditText password = view.findViewById(R.id.password);
            Button next = view.findViewById(R.id.next);
            next.setOnClickListener(v -> {
                dismiss();
                node.getCallback(NameCallback.class)
                        .setName(username.getText().toString());
                node.getCallback(PasswordCallback.class)
                        .setPassword(password.getText().toString().toCharArray());
                node.next(getContext(), listener);
            });
            Button cancel = view.findViewById(R.id.cancel);
            cancel.setOnClickListener(v -> {
                dismiss();
            });
            return view;
        }
    
        @Override
        public void onAttach(@NonNull Context context) {
            super.onAttach(context);
            if (context instanceof MainActivity) {
                listener = (MainActivity) context;
            }
        }
    }
  6. Update the MainActivity.onCallbackReceived() method to launch the fragment when invoked with a Node:

    @Override
    public void onCallbackReceived(Node node) {
        NodeDialogFragment fragment = NodeDialogFragment.newInstance(node);
        fragment.show(getSupportFragmentManager(), NodeDialogFragment.class.getName());
    }
    Full source code for MainActivity
    package com.example.forgerocksdktutorial;
    
    import androidx.appcompat.app.AppCompatActivity;
    
    import android.os.Bundle;
    import android.widget.Button;
    import android.widget.TextView;
    
    import org.forgerock.android.auth.FRAuth;
    import org.forgerock.android.auth.FRUser;
    import org.forgerock.android.auth.Logger;
    import org.forgerock.android.auth.Node;
    import org.forgerock.android.auth.NodeListener;
    
    public class MainActivity extends AppCompatActivity implements NodeListener<FRUser> {
        private TextView status;
        private Button loginButton;
        private Button logoutButton;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Logger.set(Logger.Level.DEBUG);
            FRAuth.start(this);
            status = findViewById(R.id.textViewUserStatus);
            loginButton = findViewById(R.id.buttonLogin);
            logoutButton = findViewById(R.id.buttonLogout);
            updateStatus();
            loginButton.setOnClickListener(view -> FRUser.login(getApplicationContext(), this));
            logoutButton.setOnClickListener(view -> {
                FRUser.getCurrentUser().logout();
                updateStatus();
            });
        }
    
        private void updateStatus() {
            runOnUiThread(() -> {
                if (FRUser.getCurrentUser() == null) {
                    status.setText(R.string.user_not_authenticated);
                    loginButton.setEnabled(true);
                    logoutButton.setEnabled(false);
                } else {
                    status.setText(R.string.user_authenticated);
                    loginButton.setEnabled(false);
                    logoutButton.setEnabled(true);
                }
            });
        }
    
        @Override
        public void onSuccess(FRUser result) {
            updateStatus();
        }
    
        @Override
        public void onException(Exception e) {
            Logger.error(TAG, e.getMessage(), e);
        }
    
        @Override
        public void onCallbackReceived(Node node) {
            NodeDialogFragment fragment = NodeDialogFragment.newInstance(node);
            fragment.show(getSupportFragmentManager(), NodeDialogFragment.class.getName());
        }
    }
Copyright © 2010-2022 ForgeRock, all rights reserved.