Skip to content

Commit 42bb217

Browse files
authored
feat: enables OIDC auth code flow (#522)
* 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
1 parent 1e26ef3 commit 42bb217

File tree

7 files changed

+280
-17
lines changed

7 files changed

+280
-17
lines changed

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

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@
1818

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

21+
import com.google.api.client.json.GenericJson;
2122
import com.google.api.client.util.Key;
2223
import com.google.common.base.Strings;
24+
import java.util.HashMap;
25+
import java.util.Map;
2326

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

37+
@Key("clientSecret")
38+
private String clientSecret;
39+
3440
@Key("issuer")
3541
private String issuer;
3642

43+
@Key("responseType")
44+
private GenericJson responseType;
45+
3746
public String getClientId() {
3847
return clientId;
3948
}
4049

50+
public String getClientSecret() {
51+
return clientSecret;
52+
}
53+
4154
public String getIssuer() {
4255
return issuer;
4356
}
4457

58+
public boolean isCodeResponseType() {
59+
return (responseType.containsKey("code") && (boolean) responseType.get("code"));
60+
}
61+
62+
public boolean isIdTokenResponseType() {
63+
return (responseType.containsKey("idToken") && (boolean) responseType.get("idToken"));
64+
}
65+
4566
/**
4667
* Returns a new {@link UpdateRequest}, which can be used to update the attributes of this
4768
* provider config.
@@ -58,6 +79,13 @@ static void checkOidcProviderId(String providerId) {
5879
"Invalid OIDC provider ID (must be prefixed with 'oidc.'): " + providerId);
5980
}
6081

82+
static Map<String, Boolean> ensureResponseType(Map<String,Object> properties) {
83+
if (properties.get("responseType") == null) {
84+
properties.put("responseType", new HashMap<String, Boolean>());
85+
}
86+
return (Map<String, Boolean>) properties.get("responseType");
87+
}
88+
6189
/**
6290
* A specification class for creating a new OIDC Auth provider.
6391
*
@@ -99,6 +127,19 @@ public CreateRequest setClientId(String clientId) {
99127
return this;
100128
}
101129

130+
/**
131+
* Sets the client secret for the new provider. This is required for the code flow.
132+
*
133+
* @param clientSecret A non-null, non-empty client secret string.
134+
* @throws IllegalArgumentException If the client secret is null or empty.
135+
*/
136+
public CreateRequest setClientSecret(String clientSecret) {
137+
checkArgument(!Strings.isNullOrEmpty(clientSecret),
138+
"Client Secret must not be null or empty.");
139+
properties.put("clientSecret", clientSecret);
140+
return this;
141+
}
142+
102143
/**
103144
* Sets the issuer for the new provider.
104145
*
@@ -113,6 +154,36 @@ public CreateRequest setIssuer(String issuer) {
113154
return this;
114155
}
115156

157+
/**
158+
* Sets whether to enable the code response flow for the new provider. By default, this is not
159+
* enabled if no response type is specified.
160+
*
161+
* <p>A client secret must be set for this response type.
162+
*
163+
* <p>Having both the code and ID token response flows is currently not supported.
164+
*
165+
* @param enabled A boolean signifying whether the code response type is supported.
166+
*/
167+
public CreateRequest setCodeResponseType(boolean enabled) {
168+
Map<String, Boolean> map = ensureResponseType(properties);
169+
map.put("code", enabled);
170+
return this;
171+
}
172+
173+
/**
174+
* Sets whether to enable the ID token response flow for the new provider. By default, this is
175+
* enabled if no response type is specified.
176+
*
177+
* <p>Having both the code and ID token response flows is currently not supported.
178+
*
179+
* @param enabled A boolean signifying whether the ID token response type is supported.
180+
*/
181+
public CreateRequest setIdTokenResponseType(boolean enabled) {
182+
Map<String, Boolean> map = ensureResponseType(properties);
183+
map.put("idToken", enabled);
184+
return this;
185+
}
186+
116187
CreateRequest getThis() {
117188
return this;
118189
}
@@ -156,6 +227,19 @@ public UpdateRequest setClientId(String clientId) {
156227
return this;
157228
}
158229

230+
/**
231+
* Sets the client secret for the new provider. This is required for the code flow.
232+
*
233+
* @param clientSecret A non-null, non-empty client secret string.
234+
* @throws IllegalArgumentException If the client secret is null or empty.
235+
*/
236+
public UpdateRequest setClientSecret(String clientSecret) {
237+
checkArgument(!Strings.isNullOrEmpty(clientSecret),
238+
"Client Secret must not be null or empty.");
239+
properties.put("clientSecret", clientSecret);
240+
return this;
241+
}
242+
159243
/**
160244
* Sets the issuer for the existing provider.
161245
*
@@ -170,6 +254,36 @@ public UpdateRequest setIssuer(String issuer) {
170254
return this;
171255
}
172256

257+
/**
258+
* Sets whether to enable the code response flow for the new provider. By default, this is not
259+
* enabled if no response type is specified.
260+
*
261+
* <p>A client secret must be set for this response type.
262+
*
263+
* <p>Having both the code and ID token response flows is currently not supported.
264+
*
265+
* @param enabled A boolean signifying whether the code response type is supported.
266+
*/
267+
public UpdateRequest setCodeResponseType(boolean enabled) {
268+
Map<String, Boolean> map = ensureResponseType(properties);
269+
map.put("code", enabled);
270+
return this;
271+
}
272+
273+
/**
274+
* Sets whether to enable the ID token response flow for the new provider. By default, this is
275+
* enabled if no response type is specified.
276+
*
277+
* <p>Having both the code and ID token response flows is currently not supported.
278+
*
279+
* @param enabled A boolean signifying whether the ID token response type is supported.
280+
*/
281+
public UpdateRequest setIdTokenResponseType(boolean enabled) {
282+
Map<String, Boolean> map = ensureResponseType(properties);
283+
map.put("idToken", enabled);
284+
return this;
285+
}
286+
173287
UpdateRequest getThis() {
174288
return this;
175289
}

src/test/java/com/google/firebase/auth/FirebaseAuthIT.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -703,34 +703,51 @@ public void testOidcProviderConfigLifecycle() throws Exception {
703703
.setDisplayName("DisplayName")
704704
.setEnabled(true)
705705
.setClientId("ClientId")
706-
.setIssuer("https://oidc.com/issuer"));
706+
.setClientSecret("ClientSecret")
707+
.setIssuer("https://oidc.com/issuer")
708+
.setCodeResponseType(true)
709+
.setIdTokenResponseType(false));
710+
707711
assertEquals(providerId, config.getProviderId());
708712
assertEquals("DisplayName", config.getDisplayName());
709713
assertTrue(config.isEnabled());
710714
assertEquals("ClientId", config.getClientId());
715+
assertEquals("ClientSecret", config.getClientSecret());
711716
assertEquals("https://oidc.com/issuer", config.getIssuer());
717+
assertTrue(config.isCodeResponseType());
718+
assertFalse(config.isIdTokenResponseType());
712719

713720
// Get provider config
714721
config = auth.getOidcProviderConfigAsync(providerId).get();
715722
assertEquals(providerId, config.getProviderId());
716723
assertEquals("DisplayName", config.getDisplayName());
717724
assertTrue(config.isEnabled());
718725
assertEquals("ClientId", config.getClientId());
726+
assertEquals("ClientSecret", config.getClientSecret());
719727
assertEquals("https://oidc.com/issuer", config.getIssuer());
728+
assertTrue(config.isCodeResponseType());
729+
assertFalse(config.isIdTokenResponseType());
720730

721731
// Update provider config
722732
OidcProviderConfig.UpdateRequest updateRequest =
723733
new OidcProviderConfig.UpdateRequest(providerId)
724734
.setDisplayName("NewDisplayName")
725735
.setEnabled(false)
726736
.setClientId("NewClientId")
727-
.setIssuer("https://oidc.com/new-issuer");
737+
.setClientSecret("NewClientSecret")
738+
.setIssuer("https://oidc.com/new-issuer")
739+
.setCodeResponseType(false)
740+
.setIdTokenResponseType(true);
741+
728742
config = auth.updateOidcProviderConfigAsync(updateRequest).get();
729743
assertEquals(providerId, config.getProviderId());
730744
assertEquals("NewDisplayName", config.getDisplayName());
731745
assertFalse(config.isEnabled());
732746
assertEquals("NewClientId", config.getClientId());
747+
assertEquals("NewClientSecret", config.getClientSecret());
733748
assertEquals("https://oidc.com/new-issuer", config.getIssuer());
749+
assertTrue(config.isIdTokenResponseType());
750+
assertFalse(config.isCodeResponseType());
734751

735752
// Delete provider config
736753
temporaryProviderConfig.deleteOidcProviderConfig(providerId);

src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1496,7 +1496,10 @@ public void testCreateOidcProvider() throws Exception {
14961496
.setDisplayName("DISPLAY_NAME")
14971497
.setEnabled(true)
14981498
.setClientId("CLIENT_ID")
1499-
.setIssuer("https://oidc.com/issuer");
1499+
.setClientSecret("CLIENT_SECRET")
1500+
.setIssuer("https://oidc.com/issuer")
1501+
.setCodeResponseType(true)
1502+
.setIdTokenResponseType(true);
15001503

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

@@ -1507,7 +1510,13 @@ public void testCreateOidcProvider() throws Exception {
15071510
assertEquals("DISPLAY_NAME", parsed.get("displayName"));
15081511
assertTrue((boolean) parsed.get("enabled"));
15091512
assertEquals("CLIENT_ID", parsed.get("clientId"));
1513+
assertEquals("CLIENT_SECRET", parsed.get("clientSecret"));
15101514
assertEquals("https://oidc.com/issuer", parsed.get("issuer"));
1515+
1516+
Map<String, Boolean> responseType = (Map<String, Boolean>) parsed.get("responseType");
1517+
assertTrue(responseType.get("code"));
1518+
assertTrue(responseType.get("idToken"));
1519+
15111520
GenericUrl url = interceptor.getResponse().getRequest().getUrl();
15121521
assertEquals("oidc.provider-id", url.getFirst("oauthIdpConfigId"));
15131522
}
@@ -1521,7 +1530,10 @@ public void testCreateOidcProviderAsync() throws Exception {
15211530
.setDisplayName("DISPLAY_NAME")
15221531
.setEnabled(true)
15231532
.setClientId("CLIENT_ID")
1524-
.setIssuer("https://oidc.com/issuer");
1533+
.setClientSecret("CLIENT_SECRET")
1534+
.setIssuer("https://oidc.com/issuer")
1535+
.setCodeResponseType(true)
1536+
.setIdTokenResponseType(true);
15251537

15261538
OidcProviderConfig config =
15271539
FirebaseAuth.getInstance().createOidcProviderConfigAsync(createRequest).get();
@@ -1533,7 +1545,13 @@ public void testCreateOidcProviderAsync() throws Exception {
15331545
assertEquals("DISPLAY_NAME", parsed.get("displayName"));
15341546
assertTrue((boolean) parsed.get("enabled"));
15351547
assertEquals("CLIENT_ID", parsed.get("clientId"));
1548+
assertEquals("CLIENT_SECRET", parsed.get("clientSecret"));
15361549
assertEquals("https://oidc.com/issuer", parsed.get("issuer"));
1550+
1551+
Map<String, Boolean> responseType = (Map<String, Boolean>) parsed.get("responseType");
1552+
assertTrue(responseType.get("code"));
1553+
assertTrue(responseType.get("idToken"));
1554+
15371555
GenericUrl url = interceptor.getResponse().getRequest().getUrl();
15381556
assertEquals("oidc.provider-id", url.getFirst("oauthIdpConfigId"));
15391557
}
@@ -1736,7 +1754,10 @@ public void testTenantAwareUpdateOidcProvider() throws Exception {
17361754
.setDisplayName("DISPLAY_NAME")
17371755
.setEnabled(true)
17381756
.setClientId("CLIENT_ID")
1739-
.setIssuer("https://oidc.com/issuer");
1757+
.setClientSecret("CLIENT_SECRET")
1758+
.setIssuer("https://oidc.com/issuer")
1759+
.setCodeResponseType(true)
1760+
.setIdTokenResponseType(true);
17401761

17411762
OidcProviderConfig config = tenantAwareAuth.updateOidcProviderConfig(request);
17421763

@@ -1745,12 +1766,18 @@ public void testTenantAwareUpdateOidcProvider() throws Exception {
17451766
String expectedUrl = TENANTS_BASE_URL + "/TENANT_ID/oauthIdpConfigs/oidc.provider-id";
17461767
checkUrl(interceptor, "PATCH", expectedUrl);
17471768
GenericUrl url = interceptor.getResponse().getRequest().getUrl();
1748-
assertEquals("clientId,displayName,enabled,issuer", url.getFirst("updateMask"));
1769+
assertEquals("clientId,clientSecret,displayName,enabled,issuer,responseType.code,"
1770+
+ "responseType.idToken", url.getFirst("updateMask"));
17491771
GenericJson parsed = parseRequestContent(interceptor);
17501772
assertEquals("DISPLAY_NAME", parsed.get("displayName"));
17511773
assertTrue((boolean) parsed.get("enabled"));
17521774
assertEquals("CLIENT_ID", parsed.get("clientId"));
1775+
assertEquals("CLIENT_SECRET", parsed.get("clientSecret"));
17531776
assertEquals("https://oidc.com/issuer", parsed.get("issuer"));
1777+
1778+
Map<String, Boolean> responseType = (Map<String, Boolean>) parsed.get("responseType");
1779+
assertTrue(responseType.get("code"));
1780+
assertTrue(responseType.get("idToken"));
17541781
}
17551782

17561783
@Test
@@ -2825,7 +2852,10 @@ private static void checkOidcProviderConfig(OidcProviderConfig config, String pr
28252852
assertEquals("DISPLAY_NAME", config.getDisplayName());
28262853
assertTrue(config.isEnabled());
28272854
assertEquals("CLIENT_ID", config.getClientId());
2855+
assertEquals("CLIENT_SECRET", config.getClientSecret());
28282856
assertEquals("https://oidc.com/issuer", config.getIssuer());
2857+
assertTrue(config.isCodeResponseType());
2858+
assertFalse(config.isIdTokenResponseType());
28292859
}
28302860

28312861
private static void checkSamlProviderConfig(SamlProviderConfig config, String providerId) {
@@ -2857,5 +2887,4 @@ private static void checkUrl(TestResponseInterceptor interceptor, String method,
28572887
private interface UserManagerOp {
28582888
void call(FirebaseAuth auth) throws Exception;
28592889
}
2860-
28612890
}

0 commit comments

Comments
 (0)