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(auth): enables OIDC auth code flow #532

Merged
merged 6 commits into from
May 11, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
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 @@ -1615,7 +1615,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 @@ -1626,7 +1629,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 @@ -1640,7 +1649,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 @@ -1652,7 +1664,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 @@ -1855,7 +1873,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 @@ -1864,12 +1885,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 @@ -2948,7 +2975,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 @@ -2980,5 +3010,4 @@ private static void checkUrl(TestResponseInterceptor interceptor, String method,
private interface UserManagerOp {
void call(FirebaseAuth auth) throws Exception;
}

}
Loading