Skip to content

Commit

Permalink
feat(auth): enables OIDC auth code flow (#532)
Browse files Browse the repository at this point in the history
* feat: enables OIDC auth code flow

* fix: remove GenericJson from public API surface

* fix: remove duplication

* fix: camel case for test cases

* fix: remove import
  • Loading branch information
lsirac authored May 11, 2021
1 parent 4170ed9 commit 6962561
Show file tree
Hide file tree
Showing 7 changed files with 280 additions and 17 deletions.
114 changes: 114 additions & 0 deletions src/main/java/com/google/firebase/auth/OidcProviderConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@

import static com.google.common.base.Preconditions.checkArgument;

import com.google.api.client.json.GenericJson;
import com.google.api.client.util.Key;
import com.google.common.base.Strings;
import java.util.HashMap;
import java.util.Map;

/**
* Contains metadata associated with an OIDC Auth provider.
Expand All @@ -31,17 +34,35 @@ public final class OidcProviderConfig extends ProviderConfig {
@Key("clientId")
private String clientId;

@Key("clientSecret")
private String clientSecret;

@Key("issuer")
private String issuer;

@Key("responseType")
private GenericJson responseType;

public String getClientId() {
return clientId;
}

public String getClientSecret() {
return clientSecret;
}

public String getIssuer() {
return issuer;
}

public boolean isCodeResponseType() {
return (responseType.containsKey("code") && (boolean) responseType.get("code"));
}

public boolean isIdTokenResponseType() {
return (responseType.containsKey("idToken") && (boolean) responseType.get("idToken"));
}

/**
* Returns a new {@link UpdateRequest}, which can be used to update the attributes of this
* provider config.
Expand All @@ -58,6 +79,13 @@ static void checkOidcProviderId(String providerId) {
"Invalid OIDC provider ID (must be prefixed with 'oidc.'): " + providerId);
}

static Map<String, Boolean> ensureResponseType(Map<String,Object> properties) {
if (properties.get("responseType") == null) {
properties.put("responseType", new HashMap<String, Boolean>());
}
return (Map<String, Boolean>) properties.get("responseType");
}

/**
* A specification class for creating a new OIDC Auth provider.
*
Expand Down Expand Up @@ -99,6 +127,19 @@ public CreateRequest setClientId(String clientId) {
return this;
}

/**
* Sets the client secret for the new provider. This is required for the code flow.
*
* @param clientSecret A non-null, non-empty client secret string.
* @throws IllegalArgumentException If the client secret is null or empty.
*/
public CreateRequest setClientSecret(String clientSecret) {
checkArgument(!Strings.isNullOrEmpty(clientSecret),
"Client Secret must not be null or empty.");
properties.put("clientSecret", clientSecret);
return this;
}

/**
* Sets the issuer for the new provider.
*
Expand All @@ -113,6 +154,36 @@ public CreateRequest setIssuer(String issuer) {
return this;
}

/**
* Sets whether to enable the code response flow for the new provider. By default, this is not
* enabled if no response type is specified.
*
* <p>A client secret must be set for this response type.
*
* <p>Having both the code and ID token response flows is currently not supported.
*
* @param enabled A boolean signifying whether the code response type is supported.
*/
public CreateRequest setCodeResponseType(boolean enabled) {
Map<String, Boolean> map = ensureResponseType(properties);
map.put("code", enabled);
return this;
}

/**
* Sets whether to enable the ID token response flow for the new provider. By default, this is
* enabled if no response type is specified.
*
* <p>Having both the code and ID token response flows is currently not supported.
*
* @param enabled A boolean signifying whether the ID token response type is supported.
*/
public CreateRequest setIdTokenResponseType(boolean enabled) {
Map<String, Boolean> map = ensureResponseType(properties);
map.put("idToken", enabled);
return this;
}

CreateRequest getThis() {
return this;
}
Expand Down Expand Up @@ -156,6 +227,19 @@ public UpdateRequest setClientId(String clientId) {
return this;
}

/**
* Sets the client secret for the new provider. This is required for the code flow.
*
* @param clientSecret A non-null, non-empty client secret string.
* @throws IllegalArgumentException If the client secret is null or empty.
*/
public UpdateRequest setClientSecret(String clientSecret) {
checkArgument(!Strings.isNullOrEmpty(clientSecret),
"Client Secret must not be null or empty.");
properties.put("clientSecret", clientSecret);
return this;
}

/**
* Sets the issuer for the existing provider.
*
Expand All @@ -170,6 +254,36 @@ public UpdateRequest setIssuer(String issuer) {
return this;
}

/**
* Sets whether to enable the code response flow for the new provider. By default, this is not
* enabled if no response type is specified.
*
* <p>A client secret must be set for this response type.
*
* <p>Having both the code and ID token response flows is currently not supported.
*
* @param enabled A boolean signifying whether the code response type is supported.
*/
public UpdateRequest setCodeResponseType(boolean enabled) {
Map<String, Boolean> map = ensureResponseType(properties);
map.put("code", enabled);
return this;
}

/**
* Sets whether to enable the ID token response flow for the new provider. By default, this is
* enabled if no response type is specified.
*
* <p>Having both the code and ID token response flows is currently not supported.
*
* @param enabled A boolean signifying whether the ID token response type is supported.
*/
public UpdateRequest setIdTokenResponseType(boolean enabled) {
Map<String, Boolean> map = ensureResponseType(properties);
map.put("idToken", enabled);
return this;
}

UpdateRequest getThis() {
return this;
}
Expand Down
21 changes: 19 additions & 2 deletions src/test/java/com/google/firebase/auth/FirebaseAuthIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -792,34 +792,51 @@ public void testOidcProviderConfigLifecycle() throws Exception {
.setDisplayName("DisplayName")
.setEnabled(true)
.setClientId("ClientId")
.setIssuer("https://oidc.com/issuer"));
.setClientSecret("ClientSecret")
.setIssuer("https://oidc.com/issuer")
.setCodeResponseType(true)
.setIdTokenResponseType(false));

assertEquals(providerId, config.getProviderId());
assertEquals("DisplayName", config.getDisplayName());
assertTrue(config.isEnabled());
assertEquals("ClientId", config.getClientId());
assertEquals("ClientSecret", config.getClientSecret());
assertEquals("https://oidc.com/issuer", config.getIssuer());
assertTrue(config.isCodeResponseType());
assertFalse(config.isIdTokenResponseType());

// Get provider config
config = auth.getOidcProviderConfigAsync(providerId).get();
assertEquals(providerId, config.getProviderId());
assertEquals("DisplayName", config.getDisplayName());
assertTrue(config.isEnabled());
assertEquals("ClientId", config.getClientId());
assertEquals("ClientSecret", config.getClientSecret());
assertEquals("https://oidc.com/issuer", config.getIssuer());
assertTrue(config.isCodeResponseType());
assertFalse(config.isIdTokenResponseType());

// Update provider config
OidcProviderConfig.UpdateRequest updateRequest =
new OidcProviderConfig.UpdateRequest(providerId)
.setDisplayName("NewDisplayName")
.setEnabled(false)
.setClientId("NewClientId")
.setIssuer("https://oidc.com/new-issuer");
.setClientSecret("NewClientSecret")
.setIssuer("https://oidc.com/new-issuer")
.setCodeResponseType(false)
.setIdTokenResponseType(true);

config = auth.updateOidcProviderConfigAsync(updateRequest).get();
assertEquals(providerId, config.getProviderId());
assertEquals("NewDisplayName", config.getDisplayName());
assertFalse(config.isEnabled());
assertEquals("NewClientId", config.getClientId());
assertEquals("NewClientSecret", config.getClientSecret());
assertEquals("https://oidc.com/new-issuer", config.getIssuer());
assertTrue(config.isIdTokenResponseType());
assertFalse(config.isCodeResponseType());

// Delete provider config
temporaryProviderConfig.deleteOidcProviderConfig(providerId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1636,7 +1636,10 @@ public void testCreateOidcProvider() throws Exception {
.setDisplayName("DISPLAY_NAME")
.setEnabled(true)
.setClientId("CLIENT_ID")
.setIssuer("https://oidc.com/issuer");
.setClientSecret("CLIENT_SECRET")
.setIssuer("https://oidc.com/issuer")
.setCodeResponseType(true)
.setIdTokenResponseType(true);

OidcProviderConfig config = FirebaseAuth.getInstance().createOidcProviderConfig(createRequest);

Expand All @@ -1647,7 +1650,13 @@ public void testCreateOidcProvider() throws Exception {
assertEquals("DISPLAY_NAME", parsed.get("displayName"));
assertTrue((boolean) parsed.get("enabled"));
assertEquals("CLIENT_ID", parsed.get("clientId"));
assertEquals("CLIENT_SECRET", parsed.get("clientSecret"));
assertEquals("https://oidc.com/issuer", parsed.get("issuer"));

Map<String, Boolean> responseType = (Map<String, Boolean>) parsed.get("responseType");
assertTrue(responseType.get("code"));
assertTrue(responseType.get("idToken"));

GenericUrl url = interceptor.getResponse().getRequest().getUrl();
assertEquals("oidc.provider-id", url.getFirst("oauthIdpConfigId"));
}
Expand All @@ -1661,7 +1670,10 @@ public void testCreateOidcProviderAsync() throws Exception {
.setDisplayName("DISPLAY_NAME")
.setEnabled(true)
.setClientId("CLIENT_ID")
.setIssuer("https://oidc.com/issuer");
.setClientSecret("CLIENT_SECRET")
.setIssuer("https://oidc.com/issuer")
.setCodeResponseType(true)
.setIdTokenResponseType(true);

OidcProviderConfig config =
FirebaseAuth.getInstance().createOidcProviderConfigAsync(createRequest).get();
Expand All @@ -1673,7 +1685,13 @@ public void testCreateOidcProviderAsync() throws Exception {
assertEquals("DISPLAY_NAME", parsed.get("displayName"));
assertTrue((boolean) parsed.get("enabled"));
assertEquals("CLIENT_ID", parsed.get("clientId"));
assertEquals("CLIENT_SECRET", parsed.get("clientSecret"));
assertEquals("https://oidc.com/issuer", parsed.get("issuer"));

Map<String, Boolean> responseType = (Map<String, Boolean>) parsed.get("responseType");
assertTrue(responseType.get("code"));
assertTrue(responseType.get("idToken"));

GenericUrl url = interceptor.getResponse().getRequest().getUrl();
assertEquals("oidc.provider-id", url.getFirst("oauthIdpConfigId"));
}
Expand Down Expand Up @@ -1876,7 +1894,10 @@ public void testTenantAwareUpdateOidcProvider() throws Exception {
.setDisplayName("DISPLAY_NAME")
.setEnabled(true)
.setClientId("CLIENT_ID")
.setIssuer("https://oidc.com/issuer");
.setClientSecret("CLIENT_SECRET")
.setIssuer("https://oidc.com/issuer")
.setCodeResponseType(true)
.setIdTokenResponseType(true);

OidcProviderConfig config = tenantAwareAuth.updateOidcProviderConfig(request);

Expand All @@ -1885,12 +1906,18 @@ public void testTenantAwareUpdateOidcProvider() throws Exception {
String expectedUrl = TENANTS_BASE_URL + "/TENANT_ID/oauthIdpConfigs/oidc.provider-id";
checkUrl(interceptor, "PATCH", expectedUrl);
GenericUrl url = interceptor.getResponse().getRequest().getUrl();
assertEquals("clientId,displayName,enabled,issuer", url.getFirst("updateMask"));
assertEquals("clientId,clientSecret,displayName,enabled,issuer,responseType.code,"
+ "responseType.idToken", url.getFirst("updateMask"));
GenericJson parsed = parseRequestContent(interceptor);
assertEquals("DISPLAY_NAME", parsed.get("displayName"));
assertTrue((boolean) parsed.get("enabled"));
assertEquals("CLIENT_ID", parsed.get("clientId"));
assertEquals("CLIENT_SECRET", parsed.get("clientSecret"));
assertEquals("https://oidc.com/issuer", parsed.get("issuer"));

Map<String, Boolean> responseType = (Map<String, Boolean>) parsed.get("responseType");
assertTrue(responseType.get("code"));
assertTrue(responseType.get("idToken"));
}

@Test
Expand Down Expand Up @@ -2969,7 +2996,10 @@ private static void checkOidcProviderConfig(OidcProviderConfig config, String pr
assertEquals("DISPLAY_NAME", config.getDisplayName());
assertTrue(config.isEnabled());
assertEquals("CLIENT_ID", config.getClientId());
assertEquals("CLIENT_SECRET", config.getClientSecret());
assertEquals("https://oidc.com/issuer", config.getIssuer());
assertTrue(config.isCodeResponseType());
assertFalse(config.isIdTokenResponseType());
}

private static void checkSamlProviderConfig(SamlProviderConfig config, String providerId) {
Expand Down Expand Up @@ -3001,6 +3031,5 @@ private static void checkUrl(TestResponseInterceptor interceptor, String method,
private interface UserManagerOp {
void call(FirebaseAuth auth) throws Exception;
}

}

Loading

0 comments on commit 6962561

Please sign in to comment.