Skip to content

Add operations to create and delete OIDC provider configs. #400

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
Show file tree
Hide file tree
Changes from 5 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
143 changes: 113 additions & 30 deletions src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@
import com.google.firebase.auth.FirebaseUserManager.UserImportRequest;
import com.google.firebase.auth.ListUsersPage.DefaultUserSource;
import com.google.firebase.auth.ListUsersPage.PageFactory;
import com.google.firebase.auth.UserRecord.CreateRequest;
import com.google.firebase.auth.UserRecord.UpdateRequest;
import com.google.firebase.auth.UserRecord;
import com.google.firebase.auth.internal.FirebaseTokenFactory;
import com.google.firebase.internal.CallableOperation;
import com.google.firebase.internal.NonNull;
Expand Down Expand Up @@ -320,7 +319,8 @@ private CallableOperation<Void, FirebaseAuthException> revokeRefreshTokensOp(fin
@Override
protected Void execute() throws FirebaseAuthException {
int currentTimeSeconds = (int) (System.currentTimeMillis() / 1000);
UpdateRequest request = new UpdateRequest(uid).setValidSince(currentTimeSeconds);
UserRecord.UpdateRequest request =
new UserRecord.UpdateRequest(uid).setValidSince(currentTimeSeconds);
userManager.updateUser(request, jsonFactory);
return null;
}
Expand Down Expand Up @@ -512,32 +512,33 @@ protected ListUsersPage execute() throws FirebaseAuthException {

/**
* Creates a new user account with the attributes contained in the specified {@link
* CreateRequest}.
* UserRecord.CreateRequest}.
*
* @param request A non-null {@link CreateRequest} instance.
* @param request A non-null {@link UserRecord.CreateRequest} instance.
* @return A {@link UserRecord} instance corresponding to the newly created account.
* @throws NullPointerException if the provided request is null.
* @throws FirebaseAuthException if an error occurs while creating the user account.
*/
public UserRecord createUser(@NonNull CreateRequest request) throws FirebaseAuthException {
public UserRecord createUser(@NonNull UserRecord.CreateRequest request)
throws FirebaseAuthException {
return createUserOp(request).call();
}

/**
* Similar to {@link #createUser(CreateRequest)} but performs the operation asynchronously.
* Similar to {@link #createUser} but performs the operation asynchronously.
*
* @param request A non-null {@link CreateRequest} instance.
* @param request A non-null {@link UserRecord.CreateRequest} instance.
* @return An {@code ApiFuture} which will complete successfully with a {@link UserRecord}
* instance corresponding to the newly created account. If an error occurs while creating the
* user account, the future throws a {@link FirebaseAuthException}.
* @throws NullPointerException if the provided request is null.
*/
public ApiFuture<UserRecord> createUserAsync(@NonNull CreateRequest request) {
public ApiFuture<UserRecord> createUserAsync(@NonNull UserRecord.CreateRequest request) {
return createUserOp(request).callAsync(firebaseApp);
}

private CallableOperation<UserRecord, FirebaseAuthException> createUserOp(
final CreateRequest request) {
final UserRecord.CreateRequest request) {
checkNotDestroyed();
checkNotNull(request, "create request must not be null");
final FirebaseUserManager userManager = getUserManager();
Expand All @@ -552,31 +553,32 @@ protected UserRecord execute() throws FirebaseAuthException {

/**
* Updates an existing user account with the attributes contained in the specified {@link
* UpdateRequest}.
* UserRecord.UpdateRequest}.
*
* @param request A non-null {@link UpdateRequest} instance.
* @param request A non-null {@link UserRecord.UpdateRequest} instance.
* @return A {@link UserRecord} instance corresponding to the updated user account.
* @throws NullPointerException if the provided update request is null.
* @throws FirebaseAuthException if an error occurs while updating the user account.
*/
public UserRecord updateUser(@NonNull UpdateRequest request) throws FirebaseAuthException {
public UserRecord updateUser(@NonNull UserRecord.UpdateRequest request)
throws FirebaseAuthException {
return updateUserOp(request).call();
}

/**
* Similar to {@link #updateUser(UpdateRequest)} but performs the operation asynchronously.
* Similar to {@link #updateUser} but performs the operation asynchronously.
*
* @param request A non-null {@link UpdateRequest} instance.
* @param request A non-null {@link UserRecord.UpdateRequest} instance.
* @return An {@code ApiFuture} which will complete successfully with a {@link UserRecord}
* instance corresponding to the updated user account. If an error occurs while updating the
* user account, the future throws a {@link FirebaseAuthException}.
*/
public ApiFuture<UserRecord> updateUserAsync(@NonNull UpdateRequest request) {
public ApiFuture<UserRecord> updateUserAsync(@NonNull UserRecord.UpdateRequest request) {
return updateUserOp(request).callAsync(firebaseApp);
}

private CallableOperation<UserRecord, FirebaseAuthException> updateUserOp(
final UpdateRequest request) {
final UserRecord.UpdateRequest request) {
checkNotDestroyed();
checkNotNull(request, "update request must not be null");
final FirebaseUserManager userManager = getUserManager();
Expand Down Expand Up @@ -636,7 +638,8 @@ private CallableOperation<Void, FirebaseAuthException> setCustomUserClaimsOp(
return new CallableOperation<Void, FirebaseAuthException>() {
@Override
protected Void execute() throws FirebaseAuthException {
final UpdateRequest request = new UpdateRequest(uid).setCustomClaims(claims);
final UserRecord.UpdateRequest request =
new UserRecord.UpdateRequest(uid).setCustomClaims(claims);
userManager.updateUser(request, jsonFactory);
return null;
}
Expand Down Expand Up @@ -917,18 +920,6 @@ public ApiFuture<String> generateSignInWithEmailLinkAsync(
.callAsync(firebaseApp);
}

FirebaseApp getFirebaseApp() {
return this.firebaseApp;
}

FirebaseTokenVerifier getCookieVerifier() {
return this.cookieVerifier.get();
}

FirebaseUserManager getUserManager() {
return this.userManager.get();
}

private CallableOperation<String, FirebaseAuthException> generateEmailActionLinkOp(
final EmailLinkType type, final String email, final ActionCodeSettings settings) {
checkNotDestroyed();
Expand All @@ -945,6 +936,98 @@ protected String execute() throws FirebaseAuthException {
};
}

/**
* Creates a new provider OIDC Auth config with the attributes contained in the specified {@link
* OidcProviderConfig.CreateRequest}.
*
* @param request A non-null {@link OidcProviderConfig.CreateRequest} instance.
* @return An {@link OidcProviderConfig} instance corresponding to the newly created provider
* config.
* @throws NullPointerException if the provided request is null.
* @throws FirebaseAuthException if an error occurs while creating the provider config.
*/
public OidcProviderConfig createOidcProviderConfig(
@NonNull OidcProviderConfig.CreateRequest request) throws FirebaseAuthException {
return createOidcProviderConfigOp(request).call();
}

/**
* Similar to {@link #createOidcProviderConfig} but performs the operation asynchronously.
*
* @param request A non-null {@link OidcProviderConfig.CreateRequest} instance.
* @return An {@code ApiFuture} which will complete successfully with a {@link OidcProviderConfig}
* instance corresponding to the newly created provider config. If an error occurs while
* creating the provider config, the future throws a {@link FirebaseAuthException}.
* @throws NullPointerException if the provided request is null.
*/
public ApiFuture<OidcProviderConfig> createOidcProviderConfigAsync(
@NonNull OidcProviderConfig.CreateRequest request) {
return createOidcProviderConfigOp(request).callAsync(firebaseApp);
}

private CallableOperation<OidcProviderConfig, FirebaseAuthException>
createOidcProviderConfigOp(final OidcProviderConfig.CreateRequest request) {
checkNotDestroyed();
checkNotNull(request, "create request must not be null");
final FirebaseUserManager userManager = getUserManager();
return new CallableOperation<OidcProviderConfig, FirebaseAuthException>() {
@Override
protected OidcProviderConfig execute() throws FirebaseAuthException {
return userManager.createOidcProviderConfig(request);
}
};
}

/**
* Deletes the provider config identified by the specified provider ID.
*
* @param providerId A provider ID string.
* @throws IllegalArgumentException If the provider ID string is null or empty.
* @throws FirebaseAuthException If an error occurs while deleting the provider config.
*/
public void deleteProviderConfig(@NonNull String providerId) throws FirebaseAuthException {
deleteProviderConfigOp(providerId).call();
}

/**
* Similar to {@link #deleteProviderConfig} but performs the operation asynchronously.
*
* @param providerId A provider ID string.
* @return An {@code ApiFuture} which will complete successfully when the specified provider
* config has been deleted. If an error occurs while deleting the provider config, the future
* throws a {@link FirebaseAuthException}.
* @throws IllegalArgumentException If the provider ID string is null or empty.
*/
public ApiFuture<Void> deleteProviderConfigAsync(String providerId) {
return deleteProviderConfigOp(providerId).callAsync(firebaseApp);
}

private CallableOperation<Void, FirebaseAuthException> deleteProviderConfigOp(
final String providerId) {
checkNotDestroyed();
checkArgument(!Strings.isNullOrEmpty(providerId), "provider ID must not be null or empty");
final FirebaseUserManager userManager = getUserManager();
return new CallableOperation<Void, FirebaseAuthException>() {
@Override
protected Void execute() throws FirebaseAuthException {
userManager.deleteProviderConfig(providerId);
return null;
}
};
}

FirebaseApp getFirebaseApp() {
return this.firebaseApp;
}

FirebaseTokenVerifier getCookieVerifier() {
return this.cookieVerifier.get();
}

FirebaseUserManager getUserManager() {
return this.userManager.get();
}

protected <T> Supplier<T> threadSafeMemoize(final Supplier<T> supplier) {
return Suppliers.memoize(
new Supplier<T>() {
Expand Down
31 changes: 22 additions & 9 deletions src/main/java/com/google/firebase/auth/AuthProviderConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,22 @@
*/
public abstract class AuthProviderConfig {

@Key("name")
// Lazily initialized from 'resourceName'.
private String providerId;

@Key("name")
private String resourceName;

@Key("displayName")
private String displayName;

@Key("enabled")
private boolean enabled;

public String getProviderId() {
if (providerId == null) {
providerId = resourceName.substring(resourceName.lastIndexOf("/") + 1);
}
return providerId;
}

Expand All @@ -56,45 +62,52 @@ public boolean isEnabled() {
* <p>Set the initial attributes of the new provider by calling various setter methods available
* in this class.
*/
public abstract static class CreateRequest {
public abstract static class CreateRequest<T extends CreateRequest<T>> {

final Map<String,Object> properties = new HashMap<>();
String providerId;

/**
* Sets the ID for the new provider.
*
* @param providerId a non-null, non-empty provider ID string.
*/
public CreateRequest setProviderId(String providerId) {
public T setProviderId(String providerId) {
checkArgument(
!Strings.isNullOrEmpty(providerId), "provider ID name must not be null or empty");
properties.put("name", providerId);
return this;
this.providerId = providerId;
return getThis();
}

String getProviderId() {
return providerId;
}

/**
* Sets the display name for the new provider.
*
* @param displayName a non-null, non-empty display name string.
*/
public CreateRequest setDisplayName(String displayName) {
public T setDisplayName(String displayName) {
checkArgument(!Strings.isNullOrEmpty(displayName), "display name must not be null or empty");
properties.put("displayName", displayName);
return this;
return getThis();
}

/**
* Sets whether to allow the user to sign in with the provider.
*
* @param enabled a boolean indicating whether the user can sign in with the provider
*/
public CreateRequest setEnabled(boolean enabled) {
public T setEnabled(boolean enabled) {
properties.put("enabled", enabled);
return this;
return getThis();
}

Map<String, Object> getProperties() {
return ImmutableMap.copyOf(properties);
}

abstract T getThis();
}
}
33 changes: 26 additions & 7 deletions src/main/java/com/google/firebase/auth/FirebaseUserManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
*/
class FirebaseUserManager {

static final String CONFIGURATION_NOT_FOUND = "project-not-found";
static final String TENANT_ID_MISMATCH_ERROR = "tenant-id-mismatch";
static final String TENANT_NOT_FOUND_ERROR = "tenant-not-found";
static final String USER_NOT_FOUND_ERROR = "user-not-found";
Expand All @@ -74,7 +75,7 @@ class FirebaseUserManager {
// SDK error codes defined at: https://firebase.google.com/docs/auth/admin/errors
private static final Map<String, String> ERROR_CODES = ImmutableMap.<String, String>builder()
.put("CLAIMS_TOO_LARGE", "claims-too-large")
.put("CONFIGURATION_NOT_FOUND", "project-not-found")
.put("CONFIGURATION_NOT_FOUND", CONFIGURATION_NOT_FOUND)
.put("INSUFFICIENT_PERMISSION", "insufficient-permission")
.put("DUPLICATE_EMAIL", "email-already-exists")
.put("DUPLICATE_LOCAL_ID", "uid-already-exists")
Expand Down Expand Up @@ -106,6 +107,7 @@ class FirebaseUserManager {
private static final String CLIENT_VERSION_HEADER = "X-Client-Version";

private final String userMgtBaseUrl;
private final String idpConfigMgtBaseUrl;
private final String tenantMgtBaseUrl;
private final JsonFactory jsonFactory;
private final HttpRequestFactory requestFactory;
Expand All @@ -120,15 +122,18 @@ class FirebaseUserManager {
"Project ID is required to access the auth service. Use a service account credential or "
+ "set the project ID explicitly via FirebaseOptions. Alternatively you can also "
+ "set the project ID via the GOOGLE_CLOUD_PROJECT environment variable.");
String tenantId = builder.tenantId;
if (builder.tenantId == null) {
this.userMgtBaseUrl = String.format(ID_TOOLKIT_URL, "v1", projectId);
final String idToolkitUrlV1 = String.format(ID_TOOLKIT_URL, "v1", projectId);
final String idToolkitUrlV2 = String.format(ID_TOOLKIT_URL, "v2", projectId);
final String tenantId = builder.tenantId;
if (tenantId == null) {
this.userMgtBaseUrl = idToolkitUrlV1;
this.idpConfigMgtBaseUrl = idToolkitUrlV2 + "/oauthIdpConfigs";
} else {
checkArgument(!tenantId.isEmpty(), "tenant ID must not be empty");
this.userMgtBaseUrl =
String.format(ID_TOOLKIT_URL, "v1", projectId) + getTenantUrlSuffix(tenantId);
this.userMgtBaseUrl = idToolkitUrlV1 + getTenantUrlSuffix(tenantId);
this.idpConfigMgtBaseUrl = idToolkitUrlV2 + getTenantUrlSuffix(tenantId) + "/oauthIdpConfigs";
}
this.tenantMgtBaseUrl = String.format(ID_TOOLKIT_URL, "v2", projectId);
this.tenantMgtBaseUrl = idToolkitUrlV2;
this.jsonFactory = app.getOptions().getJsonFactory();
this.requestFactory = builder.requestFactory == null
? ApiClientUtils.newAuthorizedRequestFactory(app) : builder.requestFactory;
Expand Down Expand Up @@ -316,6 +321,20 @@ String getEmailActionLink(EmailLinkType type, String email,
throw new FirebaseAuthException(INTERNAL_ERROR, "Failed to create email action link");
}

OidcProviderConfig createOidcProviderConfig(
OidcProviderConfig.CreateRequest request) throws FirebaseAuthException {
GenericUrl url = new GenericUrl(idpConfigMgtBaseUrl);
String providerId = request.getProviderId();
checkArgument(!Strings.isNullOrEmpty(providerId), "provider ID must not be null or empty");
url.set("oauthIdpConfigId", providerId);
return sendRequest("POST", url, request.getProperties(), OidcProviderConfig.class);
}

void deleteProviderConfig(String providerId) throws FirebaseAuthException {
GenericUrl url = new GenericUrl(idpConfigMgtBaseUrl + "/" + providerId);
sendRequest("DELETE", url, null, GenericJson.class);
}

private static String getTenantUrlSuffix(String tenantId) {
checkArgument(!Strings.isNullOrEmpty(tenantId));
return "/tenants/" + tenantId;
Expand Down
Loading