Skip to content

Commit 0a0662e

Browse files
authored
feat(auth): Add tenant operations, tenant-aware user operations, and provider config operations (#395)
* Pull parts of FirebaseAuth into an abstract class. (#352) This moves parts of FirebaseAuth into an abstract class as part of adding multi-tenancy support. * Add Tenant class and its create and update request classes. (#344) This pull request adds the Tenant class (including it's create/update inner classes) as part of adding multi-tenancy support. * Add ListTenantsPage class. (#358) Add ListTenantsPage and some supporting code as part of adding multi-tenancy support. This code was very largely based off of ListUsersPage and ListUsersPageTest. * Add updateRequest method to Tenant class and add unit tests. (#361) Added some things to the Tenant class and added a few unit tests. This is part of the initiative to adding multi-tenancy support (see issue #332). * Create TenantManager class and wire through listTenants operation. (#369) Add the TenantManager class and wire through the listTenants operation. Also add unit tests to FirebaseUserManagerTest. * Add deleteTenant operation to TenantManager. (#372) This adds deleteTenant to the TenantManager class. I've added the relevant unit tests to FirebaseUserManagerTest. This is part of the initiative to adding multi-tenancy support (see issue #332). * Add getTenant operation to TenantManager. (#371) Added getTenant to the TenantManager class. Also added the relevant unit tests to FirebaseUserManagerTest. This is part of the initiative to adding multi-tenancy support (see issue #332). * Add createTenant and updateTenant operations. (#377) Added createTenant and updateTenant to the TenantManager class. Also added the relevant unit tests to FirebaseUserManagerTest. This is part of the initiative to adding multi-tenancy support (see issue #332). * Add integration tests for TenantManager operations. (#385) This adds some integration testing for all of the tenant operations in TenantManager. Several bugs were uncovered after running the tests, so these have been fixed. This is part of the initiative to adding multi-tenancy support (see issue #332). * Add firebase auth destroy check before tenant operations. (#386) This addresses some TODOs left as part of the initiative to add multi-tenancy support (see issue #332). * Make user operations tenant-aware. (#387) This makes user operations tenant-aware. I've added some integration tests to ensure that this is working correctly. This is part of the initiative to adding multi-tenancy support (see issue #332). * Remove unused AutoValue dependency. (#392) Remove unused AutoValue dependency (and remove Java 8 API dependency which was accidentally introduced). * Indicate how to get set up for the multitenancy integration tests. (#393) This documentation is based off of the instructions in https://github.com/firebase/firebase-admin-node/blob/master/CONTRIBUTING.md. * Add tenant-aware token generation and verification. (#391) This incorporates the tenant ID into the token generation and validation when using a tenant-aware client. This is part of the initiative to add multi-tenancy support (see issue #332). * Fix javadoc comment. * Trigger CI * Make several Op methods private. * Move createSessionCookie and verifySessionCookie back to FirebaseAuth. * Make verifySessionCookieOp private. * Fix a few javadoc comments. * Address Kevin's feedback. * Make TenantAwareFirebaseAuth final. * chore: Merging master into tenant-mgt (#422) * Fixed a bad merge * Add provider config management operations. (#433) Adds all of the OIDC and SAML provider config operations, related to adding multi-tenancy support. * Stop using deprecated MockHttpTransport.builder() method. * Moved tenant management code into a new package (#449) * Multi-tenancy refactor experiment * fix(auth): Completed tenant mgt refactor * Added license header to new class * Responding to code review comments: Consolidated error codes in AuthHttpClient * Improve unit test coverage of tenant/provider-related code (#453) I've improved the unit test coverage of tenant/provider-related code, and I've also removed a number of unused imports. * Fix integration tests.
1 parent eeee30b commit 0a0662e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+9028
-1729
lines changed

CONTRIBUTING.md

+13-4
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,17 @@ Authentication Admin` role at
141141
[Google Cloud Platform Console / IAM & admin](https://console.cloud.google.com/iam-admin). This is
142142
required to ensure that exported user records contain the password hashes of the user accounts.
143143
Also obtain the web API key of the project from the "Settings > General" page, and save it as
144-
`integration_apikey.txt` at the root of the codebase. Now run the following command to invoke the
145-
integration test suite:
144+
`integration_apikey.txt` at the root of the codebase.
145+
146+
Some of the integration tests require an
147+
[Identity Platform](https://cloud.google.com/identity-platform/) project with multi-tenancy
148+
[enabled](https://cloud.google.com/identity-platform/docs/multi-tenancy-quickstart#enabling_multi-tenancy).
149+
An existing Firebase project can be upgraded to an Identity Platform project without losing any
150+
functionality via the
151+
[Identity Platform Marketplace Page](https://console.cloud.google.com/customer-identity). Note that
152+
charges may be incurred for active users beyond the Identity Platform free tier.
153+
154+
Now run the following command to invoke the integration test suite:
146155

147156
```
148157
mvn verify
@@ -153,14 +162,14 @@ tests, specify the `-DskipUTs` flag.
153162

154163
### Generating API Docs
155164

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

159168
```
160169
mvn javadoc:javadoc
161170
```
162171

163-
This will generate the API docs, and place them in the `target/site/apidocs` directory.
172+
This will generate the API docs, and place them in the `target/site/apidocs` directory.
164173

165174
To generate API docs for only the public APIs (i.e. ones that are not marked with `@hide` tags),
166175
you need to trigger the `devsite-apidocs` Maven profile. This profile uses Maven Javadoc plugin

checkstyle.xml

+6-3
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,15 @@
4242
<module name="FileTabCharacter">
4343
<property name="eachLine" value="true"/>
4444
</module>
45-
45+
4646
<module name="SuppressionCommentFilter">
4747
<property name="offCommentFormat" value="CSOFF\: ([\w\|]+)"/>
4848
<property name="onCommentFormat" value="CSON\: ([\w\|]+)"/>
4949
<property name="checkFormat" value="$1"/>
5050
</module>
5151

52-
<module name="TreeWalker">
53-
<module name="FileContentsHolder"/>
52+
<module name="TreeWalker">
53+
<module name="FileContentsHolder"/>
5454
<module name="OuterTypeFilename"/>
5555
<module name="IllegalTokenText">
5656
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
@@ -229,6 +229,9 @@
229229
<property name="allowedAnnotations" value="Override, Test"/>
230230
<property name="allowThrowsTagsForSubclasses" value="true"/>
231231
<property name="allowMissingJavadoc" value="true"/>
232+
<!-- Setting this property helps avoid some strange errors. For more information, see -->
233+
<!-- https://stackoverflow.com/questions/27938039/unable-to-get-class-information-for-checkstyle. -->
234+
<property name="suppressLoadErrors" value="true"/>
232235
</module>
233236
<module name="MethodName">
234237
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/>

src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java

+1,723
Large diffs are not rendered by default.

src/main/java/com/google/firebase/auth/FirebaseAuth.java

+69-1,162
Large diffs are not rendered by default.

src/main/java/com/google/firebase/auth/FirebaseToken.java

+11-2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,15 @@ public String getUid() {
4242
return (String) claims.get("sub");
4343
}
4444

45+
/** Returns the tenant ID for the this token. */
46+
public String getTenantId() {
47+
Map<String, Object> firebase = (Map<String, Object>) claims.get("firebase");
48+
if (firebase == null) {
49+
return null;
50+
}
51+
return (String) firebase.get("tenant");
52+
}
53+
4554
/** Returns the Issuer for the this token. */
4655
public String getIssuer() {
4756
return (String) claims.get("iss");
@@ -57,14 +66,14 @@ public String getPicture() {
5766
return (String) claims.get("picture");
5867
}
5968

60-
/**
69+
/**
6170
* Returns the e-mail address for this user, or {@code null} if it's unavailable.
6271
*/
6372
public String getEmail() {
6473
return (String) claims.get("email");
6574
}
6675

67-
/**
76+
/**
6877
* Indicates if the email address returned by {@link #getEmail()} has been verified as good.
6978
*/
7079
public boolean isEmailVerified() {

src/main/java/com/google/firebase/auth/FirebaseTokenUtils.java

+14-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import com.google.firebase.ImplFirebaseTrampolines;
3131
import com.google.firebase.auth.internal.CryptoSigners;
3232
import com.google.firebase.auth.internal.FirebaseTokenFactory;
33+
import com.google.firebase.internal.Nullable;
3334

3435
import java.io.IOException;
3536

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

5455
static FirebaseTokenFactory createTokenFactory(FirebaseApp firebaseApp, Clock clock) {
56+
return createTokenFactory(firebaseApp, clock, null);
57+
}
58+
59+
static FirebaseTokenFactory createTokenFactory(
60+
FirebaseApp firebaseApp, Clock clock, @Nullable String tenantId) {
5561
try {
5662
return new FirebaseTokenFactory(
5763
firebaseApp.getOptions().getJsonFactory(),
5864
clock,
59-
CryptoSigners.getCryptoSigner(firebaseApp));
65+
CryptoSigners.getCryptoSigner(firebaseApp),
66+
tenantId);
6067
} catch (IOException e) {
6168
throw new IllegalStateException(
6269
"Failed to initialize FirebaseTokenFactory. Make sure to initialize the SDK "
@@ -68,6 +75,11 @@ static FirebaseTokenFactory createTokenFactory(FirebaseApp firebaseApp, Clock cl
6875
}
6976

7077
static FirebaseTokenVerifierImpl createIdTokenVerifier(FirebaseApp app, Clock clock) {
78+
return createIdTokenVerifier(app, clock, null);
79+
}
80+
81+
static FirebaseTokenVerifierImpl createIdTokenVerifier(
82+
FirebaseApp app, Clock clock, @Nullable String tenantId) {
7183
String projectId = ImplFirebaseTrampolines.getProjectId(app);
7284
checkState(!Strings.isNullOrEmpty(projectId),
7385
"Must initialize FirebaseApp with a project ID to call verifyIdToken()");
@@ -82,6 +94,7 @@ static FirebaseTokenVerifierImpl createIdTokenVerifier(FirebaseApp app, Clock cl
8294
.setJsonFactory(app.getOptions().getJsonFactory())
8395
.setPublicKeysManager(publicKeysManager)
8496
.setIdTokenVerifier(idTokenVerifier)
97+
.setTenantId(tenantId)
8598
.build();
8699
}
87100

src/main/java/com/google/firebase/auth/FirebaseTokenVerifierImpl.java

+25-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import com.google.api.client.util.ArrayMap;
2929
import com.google.common.base.Joiner;
3030
import com.google.common.base.Strings;
31+
import com.google.firebase.internal.Nullable;
3132
import java.io.IOException;
3233
import java.math.BigDecimal;
3334
import java.security.GeneralSecurityException;
@@ -45,6 +46,7 @@ final class FirebaseTokenVerifierImpl implements FirebaseTokenVerifier {
4546
"https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit";
4647
private static final String ERROR_INVALID_CREDENTIAL = "ERROR_INVALID_CREDENTIAL";
4748
private static final String ERROR_RUNTIME_EXCEPTION = "ERROR_RUNTIME_EXCEPTION";
49+
static final String TENANT_ID_MISMATCH_ERROR = "tenant-id-mismatch";
4850

4951
private final JsonFactory jsonFactory;
5052
private final GooglePublicKeysManager publicKeysManager;
@@ -53,6 +55,7 @@ final class FirebaseTokenVerifierImpl implements FirebaseTokenVerifier {
5355
private final String shortName;
5456
private final String articledShortName;
5557
private final String docUrl;
58+
private final String tenantId;
5659

5760
private FirebaseTokenVerifierImpl(Builder builder) {
5861
this.jsonFactory = checkNotNull(builder.jsonFactory);
@@ -65,6 +68,7 @@ private FirebaseTokenVerifierImpl(Builder builder) {
6568
this.shortName = builder.shortName;
6669
this.articledShortName = prefixWithIndefiniteArticle(this.shortName);
6770
this.docUrl = builder.docUrl;
71+
this.tenantId = Strings.nullToEmpty(builder.tenantId);
6872
}
6973

7074
/**
@@ -90,7 +94,9 @@ public FirebaseToken verifyToken(String token) throws FirebaseAuthException {
9094
IdToken idToken = parse(token);
9195
checkContents(idToken);
9296
checkSignature(idToken);
93-
return new FirebaseToken(idToken.getPayload());
97+
FirebaseToken firebaseToken = new FirebaseToken(idToken.getPayload());
98+
checkTenantId(firebaseToken);
99+
return firebaseToken;
94100
}
95101

96102
GooglePublicKeysManager getPublicKeysManager() {
@@ -278,6 +284,18 @@ private boolean containsLegacyUidField(IdToken.Payload payload) {
278284
return false;
279285
}
280286

287+
private void checkTenantId(final FirebaseToken firebaseToken) throws FirebaseAuthException {
288+
String tokenTenantId = Strings.nullToEmpty(firebaseToken.getTenantId());
289+
if (!this.tenantId.equals(tokenTenantId)) {
290+
throw new FirebaseAuthException(
291+
TENANT_ID_MISMATCH_ERROR,
292+
String.format(
293+
"The tenant ID ('%s') of the token did not match the expected value ('%s')",
294+
tokenTenantId,
295+
tenantId));
296+
}
297+
}
298+
281299
static Builder builder() {
282300
return new Builder();
283301
}
@@ -290,6 +308,7 @@ static final class Builder {
290308
private String shortName;
291309
private IdTokenVerifier idTokenVerifier;
292310
private String docUrl;
311+
private String tenantId;
293312

294313
private Builder() { }
295314

@@ -323,6 +342,11 @@ Builder setDocUrl(String docUrl) {
323342
return this;
324343
}
325344

345+
Builder setTenantId(@Nullable String tenantId) {
346+
this.tenantId = tenantId;
347+
return this;
348+
}
349+
326350
FirebaseTokenVerifierImpl build() {
327351
return new FirebaseTokenVerifierImpl(this);
328352
}

0 commit comments

Comments
 (0)