Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: rich consents #122

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 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
81 changes: 56 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Guardian SDK for Android
============
# Guardian SDK for Android

[![CircleCI](https://img.shields.io/circleci/project/github/auth0/Guardian.Android.svg)](https://circleci.com/gh/auth0/Guardian.Android)
[![Coverage Status](https://img.shields.io/codecov/c/github/auth0/Guardian.Android/master.svg)](https://codecov.io/github/auth0/Guardian.Android)
[![License](http://img.shields.io/:license-mit-blue.svg)](http://doge.mit-license.org)
Expand Down Expand Up @@ -30,7 +30,7 @@ credentials, otherwise you would not receive any push notifications. Please read

GuardianSDK is available both in [Maven Central](http://search.maven.org) and
[JCenter](https://bintray.com/bintray/jcenter).
To start using *GuardianSDK* add these lines to your `build.gradle` dependencies file:
To start using _GuardianSDK_ add these lines to your `build.gradle` dependencies file:

```gradle
implementation 'com.auth0.android:guardian:0.8.0'
Expand Down Expand Up @@ -113,11 +113,11 @@ guardian
The `deviceName` and `fcmToken` are data that you must provide:

- The `deviceName` is the name that you want for the enrollment. It will be displayed to the user
when the second factor is required.
when the second factor is required.

- The FCM token is the token for Firebase Cloud Messaging push notification service. In case your app
is not yet using FCM or you're not familiar with it, you should check their
[docs](https://firebase.google.com/docs/cloud-messaging/android/client#sample-register).
is not yet using FCM or you're not familiar with it, you should check their
[docs](https://firebase.google.com/docs/cloud-messaging/android/client#sample-register).

### Unenroll

Expand All @@ -136,8 +136,8 @@ Once you have the enrollment in place, you will receive a FCM push notification
has to validate his identity with MFA.

Guardian provides a method to parse the `Map<String, String>` data inside the
[RemoteMessage](https://firebase.google.com/docs/reference/android/com/google/firebase/messaging/RemoteMessage)
received from FCM and return a `Notification` instance ready to be used.
[RemoteMessage](https://firebase.google.com/docs/reference/android/com/google/firebase/messaging/RemoteMessage)
received from FCM and return a `Notification` instance ready to be used.

```java
// at your FCM listener you receive a RemoteMessage
Expand All @@ -154,12 +154,12 @@ public void onMessageReceived(RemoteMessage message) {
```

> If the `RemoteMessage` you receive is not from a Guardian notification this method will return null,
so you should always check before using it.
> so you should always check before using it.
Once you have the notification instance, you can easily allow the authentication request by using the
`allow` method. You'll also need the enrollment that you obtained previously. In case you have more
than one enrollment, you'll have to find the one that has the same id as the notification (you can
get the enrollment id with `getEnrollmentId()`.
`allow` method. You'll also need the enrollment that you obtained previously. In case you have more
than one enrollment, you'll have to find the one that has the same id as the notification (you can
get the enrollment id with `getEnrollmentId()`.

```java
guardian
Expand All @@ -178,23 +178,54 @@ guardian
.execute(); // or start(new Callback<> ...) asynchronously
```

### Fetch rich consent details

When you receive a push notification, the presence of the property `tranactionLinkingId` indicates a
rich consent record may be associated to the tranaction.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: transactionLinkingId


Copy link

@sam-muncke sam-muncke Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo:transaction

To fetch the rich consent details, you can use the `fetchConsent` method.

```java
if (notification.getTranactionLinkingId() != null) {
guardian

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: getTransactionLinkingId

.fetchConsent(notification, enrollment)
.start(new Callback<Enrollment> {
@Override
void onSuccess(RichConsent consentDetails) {
// we have the consent details
}

@Override
void onFailure(Throwable exception) {
if (exception instanceof GuardianException) {
GuardianException guardianException = (GuardianException) exception;
if (guardianException.isResourceNotFound()) {
// there is no consent associated with the tranaction
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: transaction

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for spotting these. I fixed this and the rest of typos in 6eea956

}
// something went wrong
}
});
}
```

## What is Auth0?

Auth0 helps you to:

* Add authentication with [multiple authentication sources](https://docs.auth0.com/identityproviders),
either social like **Google, Facebook, Microsoft Account, LinkedIn, GitHub, Twitter, Box, Salesforce,
among others**, or enterprise identity systems like **Windows Azure AD, Google Apps, Active Directory,
ADFS or any SAML Identity Provider**.
* Add authentication through more traditional
**[username/password databases](https://docs.auth0.com/mysql-connection-tutorial)**.
* Add support for **[linking different user accounts](https://docs.auth0.com/link-accounts)** with
the same user.
* Support for generating signed [Json Web Tokens](https://docs.auth0.com/jwt) to call your APIs and
**flow the user identity** securely.
* Analytics of how, when and where users are logging in.
* Pull data from other sources and add it to the user profile, through
[JavaScript rules](https://docs.auth0.com/rules).
- Add authentication with [multiple authentication sources](https://docs.auth0.com/identityproviders),
either social like **Google, Facebook, Microsoft Account, LinkedIn, GitHub, Twitter, Box, Salesforce,
among others**, or enterprise identity systems like **Windows Azure AD, Google Apps, Active Directory,
ADFS or any SAML Identity Provider**.
- Add authentication through more traditional
**[username/password databases](https://docs.auth0.com/mysql-connection-tutorial)**.
- Add support for **[linking different user accounts](https://docs.auth0.com/link-accounts)** with
the same user.
- Support for generating signed [Json Web Tokens](https://docs.auth0.com/jwt) to call your APIs and
**flow the user identity** securely.
- Analytics of how, when and where users are logging in.
- Pull data from other sources and add it to the user profile, through
[JavaScript rules](https://docs.auth0.com/rules).

## Create a free account in Auth0

Expand Down
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
namespace 'com.auth0.guardian.sample'
}

dependencies {
Expand Down
9 changes: 7 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.auth0.guardian.sample">
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
Expand Down Expand Up @@ -37,6 +36,12 @@

</activity>

<activity
android:name=".NotificationWithConsentDetailsActivity"
android:label="@string/title_notification_with_consent_details">

</activity>

<!-- [START FCM services] -->

<service android:name=".fcm.FcmListenerService"
Expand Down
1 change: 1 addition & 0 deletions app/src/main/java/com/auth0/guardian/sample/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ public class Constants {

public static final String ENROLLMENT = "com.auth0.guardian.sample.Constants.ENROLLMENT";
public static final String NOTIFICATION = "com.auth0.guardian.sample.Constants.NOTIFICATION";
public static final String CONSENT = "com.auth0.guardian.sample.Constants.CONSENT";
}
42 changes: 39 additions & 3 deletions app/src/main/java/com/auth0/guardian/sample/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import com.auth0.android.guardian.sdk.Guardian;
import com.auth0.android.guardian.sdk.GuardianException;
import com.auth0.android.guardian.sdk.ParcelableNotification;
import com.auth0.android.guardian.sdk.RichConsent;
import com.auth0.android.guardian.sdk.networking.Callback;
import com.auth0.guardian.sample.events.GuardianNotificationReceivedEvent;
import com.auth0.guardian.sample.fcm.FcmUtils;
Expand All @@ -50,6 +51,9 @@
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;

public class MainActivity extends AppCompatActivity implements FcmUtils.FcmTokenListener {

private static final String TAG = MainActivity.class.getName();
Expand Down Expand Up @@ -223,9 +227,41 @@ private void updateEnrollment(ParcelableEnrollment enrollment) {
}

private void onPushNotificationReceived(ParcelableNotification notification) {
Intent intent = NotificationActivity
.getStartIntent(this, notification, enrollment);
startActivity(intent);
Context context = this;
Intent standardNotificationActivityIntent = NotificationActivity.getStartIntent(context, notification, enrollment);

if (notification.getTransactionLinkingId() == null) {
startActivity(standardNotificationActivityIntent);
} else {
try {
guardian.fetchConsent(notification, enrollment).start(new Callback<RichConsent>() {
@Override
public void onSuccess(RichConsent consent) {
Intent intent = NotificationWithConsentDetailsActivity.getStartIntent(
context,
notification,
enrollment,
new ParcelableRichConsent(consent)
);
startActivity(intent);
}

@Override
public void onFailure(Throwable exception) {
if (exception instanceof GuardianException) {
GuardianException guardianException = (GuardianException) exception;
if (guardianException.isResourceNotFound()) {
startActivity(standardNotificationActivityIntent);
}
}
Log.e(TAG, "Error obtaining consent details", exception);

}
});
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
Log.e(TAG, "Error requesting consent details", e);
}
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package com.auth0.guardian.sample;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import com.auth0.android.guardian.sdk.Guardian;
import com.auth0.android.guardian.sdk.ParcelableNotification;
import com.auth0.android.guardian.sdk.RichConsent;
import com.auth0.android.guardian.sdk.networking.Callback;

public class NotificationWithConsentDetailsActivity extends AppCompatActivity {

private TextView bindingMessageText;
private TextView scopeText;
private TextView dateText;

private Guardian guardian;
private ParcelableEnrollment enrollment;
private ParcelableNotification notification;
private RichConsent consentDetails;

static Intent getStartIntent(@NonNull Context context,
@NonNull ParcelableNotification notification,
@NonNull ParcelableEnrollment enrollment,
@NonNull ParcelableRichConsent consent) {
if (!enrollment.getId().equals(notification.getEnrollmentId())) {
final String message = String.format("Notification doesn't match enrollment (%s != %s)",
notification.getEnrollmentId(), enrollment.getId());
throw new IllegalArgumentException(message);
}

Intent intent = new Intent(context, NotificationWithConsentDetailsActivity.class);
intent.putExtra(Constants.ENROLLMENT, enrollment);
intent.putExtra(Constants.NOTIFICATION, notification);
intent.putExtra(Constants.CONSENT, consent);
return intent;
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_notification_with_consent_details);

guardian = new Guardian.Builder()
.url(Uri.parse(getString(R.string.guardian_url)))
.enableLogging()
.build();

Intent intent = getIntent();
enrollment = intent.getParcelableExtra(Constants.ENROLLMENT);
notification = intent.getParcelableExtra(Constants.NOTIFICATION);
consentDetails = intent.getParcelableExtra(Constants.CONSENT);

setupUI();
updateUI();
}

private void setupUI() {
bindingMessageText = (TextView) findViewById(R.id.bindingMessage);
scopeText = (TextView) findViewById(R.id.scope);
dateText = (TextView) findViewById(R.id.dateText);

Button rejectButton = (Button) findViewById(R.id.rejectButton);
assert rejectButton != null;
rejectButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
rejectRequested();
}
});

Button allowButton = (Button) findViewById(R.id.allowButton);
assert allowButton != null;
allowButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
allowRequested();
}
});
}

private void updateUI() {
if (consentDetails != null) {
bindingMessageText.setText(consentDetails.getRequestedDetails().getBindingMessage());
scopeText.setText(String.join(", ", consentDetails.getRequestedDetails().getScope()));
} else {
bindingMessageText.setText("N/A");
scopeText.setText("N/A");
}
dateText.setText(notification.getDate().toString());
}

private void rejectRequested() {
guardian
.reject(notification, enrollment)
.start(new DialogCallback<>(this,
R.string.progress_title_please_wait,
R.string.progress_message_reject,
new Callback<Void>() {
@Override
public void onSuccess(Void response) {
finish();
}

@Override
public void onFailure(Throwable exception) {

}
}));
}

private void allowRequested() {
guardian
.allow(notification, enrollment)
.start(new DialogCallback<>(this,
R.string.progress_title_please_wait,
R.string.progress_message_allow,
new Callback<Void>() {
@Override
public void onSuccess(Void response) {
finish();
}

@Override
public void onFailure(Throwable exception) {

}
}));
}
}
Loading
Loading