Skip to content

feat(auth): Add tenant operations, tenant-aware user operations, and provider config operations #395

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

Merged
merged 31 commits into from
Jul 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
e30df7d
Pull parts of FirebaseAuth into an abstract class. (#352)
micahstairs Jan 30, 2020
d76fc20
Add Tenant class and its create and update request classes. (#344)
micahstairs Jan 30, 2020
f157b90
Add ListTenantsPage class. (#358)
micahstairs Feb 5, 2020
4453362
Add updateRequest method to Tenant class and add unit tests. (#361)
micahstairs Feb 17, 2020
2679904
Create TenantManager class and wire through listTenants operation. (#…
micahstairs Feb 20, 2020
a04fcfa
Add deleteTenant operation to TenantManager. (#372)
micahstairs Feb 27, 2020
c3ef972
Add getTenant operation to TenantManager. (#371)
micahstairs Feb 28, 2020
52d6cb5
Add createTenant and updateTenant operations. (#377)
micahstairs Mar 12, 2020
d4dd47f
Add integration tests for TenantManager operations. (#385)
micahstairs Mar 31, 2020
0a09db1
Add firebase auth destroy check before tenant operations. (#386)
micahstairs Apr 4, 2020
fa7c858
Make user operations tenant-aware. (#387)
micahstairs Apr 11, 2020
fadc2d3
Remove unused AutoValue dependency. (#392)
micahstairs Apr 21, 2020
ece0030
Indicate how to get set up for the multitenancy integration tests. (#…
micahstairs Apr 21, 2020
b97bb8a
Add tenant-aware token generation and verification. (#391)
micahstairs Apr 22, 2020
6c8158f
Sync to master and add builder to FirebaseUserManager.
micahstairs Apr 22, 2020
f75fe58
Fix javadoc comment.
micahstairs Apr 22, 2020
5b60738
Trigger CI
micahstairs Apr 22, 2020
b38cadc
Make several Op methods private.
micahstairs Apr 23, 2020
c90a7d5
Move createSessionCookie and verifySessionCookie back to FirebaseAuth.
micahstairs Apr 23, 2020
e7415da
Make verifySessionCookieOp private.
micahstairs Apr 23, 2020
312183a
Fix a few javadoc comments.
micahstairs Apr 23, 2020
e1a05c7
Address Kevin's feedback.
micahstairs May 5, 2020
ef3fa9f
Make TenantAwareFirebaseAuth final.
micahstairs May 18, 2020
018fa6b
chore: Merging master into tenant-mgt (#422)
hiranya911 May 19, 2020
ec15fe0
Merge branch 'master' into tenant-mgt
hiranya911 May 19, 2020
6d1b15b
Fixed a bad merge
hiranya911 May 19, 2020
b8b18f6
Add provider config management operations. (#433)
micahstairs Jun 17, 2020
baa99c3
Stop using deprecated MockHttpTransport.builder() method.
micahstairs Jun 17, 2020
042121b
Moved tenant management code into a new package (#449)
hiranya911 Jul 9, 2020
0d7a74f
Improve unit test coverage of tenant/provider-related code (#453)
micahstairs Jul 16, 2020
e66e586
Fix integration tests.
micahstairs Jul 16, 2020
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
17 changes: 13 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,17 @@ Authentication Admin` role at
[Google Cloud Platform Console / IAM & admin](https://console.cloud.google.com/iam-admin). This is
required to ensure that exported user records contain the password hashes of the user accounts.
Also obtain the web API key of the project from the "Settings > General" page, and save it as
`integration_apikey.txt` at the root of the codebase. Now run the following command to invoke the
integration test suite:
`integration_apikey.txt` at the root of the codebase.

Some of the integration tests require an
[Identity Platform](https://cloud.google.com/identity-platform/) project with multi-tenancy
[enabled](https://cloud.google.com/identity-platform/docs/multi-tenancy-quickstart#enabling_multi-tenancy).
An existing Firebase project can be upgraded to an Identity Platform project without losing any
functionality via the
[Identity Platform Marketplace Page](https://console.cloud.google.com/customer-identity). Note that
charges may be incurred for active users beyond the Identity Platform free tier.

Now run the following command to invoke the integration test suite:

```
mvn verify
Expand All @@ -153,14 +162,14 @@ tests, specify the `-DskipUTs` flag.

### Generating API Docs

Invoke the [Maven Javadoc plugin](https://maven.apache.org/plugins/maven-javadoc-plugin/) as
Invoke the [Maven Javadoc plugin](https://maven.apache.org/plugins/maven-javadoc-plugin/) as
follows to generate API docs for all packages in the codebase:

```
mvn javadoc:javadoc
```

This will generate the API docs, and place them in the `target/site/apidocs` directory.
This will generate the API docs, and place them in the `target/site/apidocs` directory.

To generate API docs for only the public APIs (i.e. ones that are not marked with `@hide` tags),
you need to trigger the `devsite-apidocs` Maven profile. This profile uses Maven Javadoc plugin
Expand Down
9 changes: 6 additions & 3 deletions checkstyle.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@
<module name="FileTabCharacter">
<property name="eachLine" value="true"/>
</module>

<module name="SuppressionCommentFilter">
<property name="offCommentFormat" value="CSOFF\: ([\w\|]+)"/>
<property name="onCommentFormat" value="CSON\: ([\w\|]+)"/>
<property name="checkFormat" value="$1"/>
</module>

<module name="TreeWalker">
<module name="FileContentsHolder"/>
<module name="TreeWalker">
<module name="FileContentsHolder"/>
<module name="OuterTypeFilename"/>
<module name="IllegalTokenText">
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
Expand Down Expand Up @@ -229,6 +229,9 @@
<property name="allowedAnnotations" value="Override, Test"/>
<property name="allowThrowsTagsForSubclasses" value="true"/>
<property name="allowMissingJavadoc" value="true"/>
<!-- Setting this property helps avoid some strange errors. For more information, see -->
<!-- https://stackoverflow.com/questions/27938039/unable-to-get-class-information-for-checkstyle. -->
<property name="suppressLoadErrors" value="true"/>
</module>
<module name="MethodName">
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/>
Expand Down
1,723 changes: 1,723 additions & 0 deletions src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java

Large diffs are not rendered by default.

1,231 changes: 69 additions & 1,162 deletions src/main/java/com/google/firebase/auth/FirebaseAuth.java

Large diffs are not rendered by default.

13 changes: 11 additions & 2 deletions src/main/java/com/google/firebase/auth/FirebaseToken.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ public String getUid() {
return (String) claims.get("sub");
}

/** Returns the tenant ID for the this token. */
public String getTenantId() {
Map<String, Object> firebase = (Map<String, Object>) claims.get("firebase");
if (firebase == null) {
return null;
}
return (String) firebase.get("tenant");
}

/** Returns the Issuer for the this token. */
public String getIssuer() {
return (String) claims.get("iss");
Expand All @@ -57,14 +66,14 @@ public String getPicture() {
return (String) claims.get("picture");
}

/**
/**
* Returns the e-mail address for this user, or {@code null} if it's unavailable.
*/
public String getEmail() {
return (String) claims.get("email");
}

/**
/**
* Indicates if the email address returned by {@link #getEmail()} has been verified as good.
*/
public boolean isEmailVerified() {
Expand Down
15 changes: 14 additions & 1 deletion src/main/java/com/google/firebase/auth/FirebaseTokenUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.google.firebase.ImplFirebaseTrampolines;
import com.google.firebase.auth.internal.CryptoSigners;
import com.google.firebase.auth.internal.FirebaseTokenFactory;
import com.google.firebase.internal.Nullable;

import java.io.IOException;

Expand All @@ -52,11 +53,17 @@ final class FirebaseTokenUtils {
private FirebaseTokenUtils() { }

static FirebaseTokenFactory createTokenFactory(FirebaseApp firebaseApp, Clock clock) {
return createTokenFactory(firebaseApp, clock, null);
}

static FirebaseTokenFactory createTokenFactory(
FirebaseApp firebaseApp, Clock clock, @Nullable String tenantId) {
try {
return new FirebaseTokenFactory(
firebaseApp.getOptions().getJsonFactory(),
clock,
CryptoSigners.getCryptoSigner(firebaseApp));
CryptoSigners.getCryptoSigner(firebaseApp),
tenantId);
} catch (IOException e) {
throw new IllegalStateException(
"Failed to initialize FirebaseTokenFactory. Make sure to initialize the SDK "
Expand All @@ -68,6 +75,11 @@ static FirebaseTokenFactory createTokenFactory(FirebaseApp firebaseApp, Clock cl
}

static FirebaseTokenVerifierImpl createIdTokenVerifier(FirebaseApp app, Clock clock) {
return createIdTokenVerifier(app, clock, null);
}

static FirebaseTokenVerifierImpl createIdTokenVerifier(
FirebaseApp app, Clock clock, @Nullable String tenantId) {
String projectId = ImplFirebaseTrampolines.getProjectId(app);
checkState(!Strings.isNullOrEmpty(projectId),
"Must initialize FirebaseApp with a project ID to call verifyIdToken()");
Expand All @@ -82,6 +94,7 @@ static FirebaseTokenVerifierImpl createIdTokenVerifier(FirebaseApp app, Clock cl
.setJsonFactory(app.getOptions().getJsonFactory())
.setPublicKeysManager(publicKeysManager)
.setIdTokenVerifier(idTokenVerifier)
.setTenantId(tenantId)
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.google.api.client.util.ArrayMap;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.firebase.internal.Nullable;
import java.io.IOException;
import java.math.BigDecimal;
import java.security.GeneralSecurityException;
Expand All @@ -45,6 +46,7 @@ final class FirebaseTokenVerifierImpl implements FirebaseTokenVerifier {
"https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit";
private static final String ERROR_INVALID_CREDENTIAL = "ERROR_INVALID_CREDENTIAL";
private static final String ERROR_RUNTIME_EXCEPTION = "ERROR_RUNTIME_EXCEPTION";
static final String TENANT_ID_MISMATCH_ERROR = "tenant-id-mismatch";

private final JsonFactory jsonFactory;
private final GooglePublicKeysManager publicKeysManager;
Expand All @@ -53,6 +55,7 @@ final class FirebaseTokenVerifierImpl implements FirebaseTokenVerifier {
private final String shortName;
private final String articledShortName;
private final String docUrl;
private final String tenantId;

private FirebaseTokenVerifierImpl(Builder builder) {
this.jsonFactory = checkNotNull(builder.jsonFactory);
Expand All @@ -65,6 +68,7 @@ private FirebaseTokenVerifierImpl(Builder builder) {
this.shortName = builder.shortName;
this.articledShortName = prefixWithIndefiniteArticle(this.shortName);
this.docUrl = builder.docUrl;
this.tenantId = Strings.nullToEmpty(builder.tenantId);
}

/**
Expand All @@ -90,7 +94,9 @@ public FirebaseToken verifyToken(String token) throws FirebaseAuthException {
IdToken idToken = parse(token);
checkContents(idToken);
checkSignature(idToken);
return new FirebaseToken(idToken.getPayload());
FirebaseToken firebaseToken = new FirebaseToken(idToken.getPayload());
checkTenantId(firebaseToken);
return firebaseToken;
}

GooglePublicKeysManager getPublicKeysManager() {
Expand Down Expand Up @@ -278,6 +284,18 @@ private boolean containsLegacyUidField(IdToken.Payload payload) {
return false;
}

private void checkTenantId(final FirebaseToken firebaseToken) throws FirebaseAuthException {
String tokenTenantId = Strings.nullToEmpty(firebaseToken.getTenantId());
if (!this.tenantId.equals(tokenTenantId)) {
throw new FirebaseAuthException(
TENANT_ID_MISMATCH_ERROR,
String.format(
"The tenant ID ('%s') of the token did not match the expected value ('%s')",
tokenTenantId,
tenantId));
}
}

static Builder builder() {
return new Builder();
}
Expand All @@ -290,6 +308,7 @@ static final class Builder {
private String shortName;
private IdTokenVerifier idTokenVerifier;
private String docUrl;
private String tenantId;

private Builder() { }

Expand Down Expand Up @@ -323,6 +342,11 @@ Builder setDocUrl(String docUrl) {
return this;
}

Builder setTenantId(@Nullable String tenantId) {
this.tenantId = tenantId;
return this;
}

FirebaseTokenVerifierImpl build() {
return new FirebaseTokenVerifierImpl(this);
}
Expand Down
Loading