Skip to content

Commit 0df0071

Browse files
authored
feat(auth): Implemented IdP config management APIs (#217)
* feat(auth): Added OidcProviderConfig type and the Get API (#200) * feat(auth): Added OidcProviderConfig type and the Get API * Added newline at eof * Fixing a typo; Updated documentation with hyperlinks * feat(auth): Implemented OidcProviderConfigArgs type and Create API (#201) * feat(auth): Implemented OidcProviderConfigArgs type and Create API * Fixing test failing on windows * Updated some comments * feat(auth): Added UpdateProviderConfigAsync() API (#202) * feat(auth): Added ListOidcProviderConfigsAsync() API (#204) * feat(auth): Added ListOidcProviderConfigsAsync() API * Using constant in error message * fix(auth): Refactored the auth provider (OIDC) management (#206) * fix(auth): Refactored the auth provider (OIDC) management * Using string interpolation in error message. * feat(auth): Added SamlProviderConfig type (#208) * feat(auth): Added SamlProviderConfig type * fix: Added newline at eof * feat(auth): Added SamlproviderConfigArgs API (#210) * feat(auth): Added support for updating SAML provider configs (#211) * feat(auth): Added support for updating SAML provider configs * fix: Added tests for null X509 cert values * fix: Moving common rpc logic to ProviderConfigClient (#212) * feat(auth): Added ListSamlProviderConfigs API (#213) * feat(auth): Added DeleteProviderConfigAsync() API (#214) * feat(auth): Added DeleteProviderConfigAsync() API * fix: Fixed the API reference text * chore: Added integration tests for SAML/OIDC IdP management (#215) * chore: Added integration tests for SAML/OIDC IdP management * chore: Trigger staging build * fix: Asserting for the ProviderId property value * fix: Updated API reference doc comments
1 parent 581b7f0 commit 0df0071

31 files changed

+4063
-38
lines changed

FirebaseAdmin/FirebaseAdmin.IntegrationTests/FirebaseAuthTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -879,7 +879,7 @@ private static async Task<string> ResetPasswordAsync(ResetPasswordRequest data)
879879
}
880880

881881
/**
882-
* The {@code batchDelete} endpoint is currently rate limited to 1qps. Use this test helper
882+
* The <c>batchDelete</c> endpoint is currently rate limited to 1qps. Use this test helper
883883
* to ensure you don't run into quota exceeded errors.
884884
*/
885885
// TODO(rsgowman): When/if the rate limit is relaxed, eliminate this helper.
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
// Copyright 2020, Google Inc. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using System;
16+
using System.Collections.Generic;
17+
using System.Linq;
18+
using System.Threading.Tasks;
19+
using FirebaseAdmin.Auth;
20+
using FirebaseAdmin.Auth.Providers;
21+
using Xunit;
22+
23+
namespace FirebaseAdmin.IntegrationTests
24+
{
25+
[TestCaseOrderer(
26+
"FirebaseAdmin.IntegrationTests.TestRankOrderer", "FirebaseAdmin.IntegrationTests")]
27+
public class OidcProviderConfigTest : IClassFixture<OidcProviderConfigFixture>
28+
{
29+
private readonly OidcProviderConfigFixture fixture;
30+
31+
public OidcProviderConfigTest(OidcProviderConfigFixture fixture)
32+
{
33+
this.fixture = fixture;
34+
}
35+
36+
[Fact]
37+
[TestRank(0)]
38+
public void CreateProviderConfig()
39+
{
40+
var config = this.fixture.ProviderConfig;
41+
42+
Assert.Equal(this.fixture.ProviderId, config.ProviderId);
43+
Assert.Equal("OIDC_DISPLAY_NAME", config.DisplayName);
44+
Assert.True(config.Enabled);
45+
Assert.Equal("OIDC_CLIENT_ID", config.ClientId);
46+
Assert.Equal("https://oidc.com/issuer", config.Issuer);
47+
}
48+
49+
[Fact]
50+
[TestRank(10)]
51+
public async Task GetProviderConfig()
52+
{
53+
var config = await FirebaseAuth.DefaultInstance.GetOidcProviderConfigAsync(
54+
this.fixture.ProviderId);
55+
56+
Assert.Equal(this.fixture.ProviderId, config.ProviderId);
57+
Assert.Equal("OIDC_DISPLAY_NAME", config.DisplayName);
58+
Assert.True(config.Enabled);
59+
Assert.Equal("OIDC_CLIENT_ID", config.ClientId);
60+
Assert.Equal("https://oidc.com/issuer", config.Issuer);
61+
}
62+
63+
[Fact]
64+
[TestRank(10)]
65+
public async Task ListProviderConfig()
66+
{
67+
OidcProviderConfig config = null;
68+
69+
var pagedEnumerable = FirebaseAuth.DefaultInstance.ListOidcProviderConfigsAsync(null);
70+
var enumerator = pagedEnumerable.GetEnumerator();
71+
while (await enumerator.MoveNext())
72+
{
73+
if (enumerator.Current.ProviderId == this.fixture.ProviderId)
74+
{
75+
config = enumerator.Current;
76+
break;
77+
}
78+
}
79+
80+
Assert.NotNull(config);
81+
Assert.Equal(this.fixture.ProviderId, config.ProviderId);
82+
Assert.Equal("OIDC_DISPLAY_NAME", config.DisplayName);
83+
Assert.True(config.Enabled);
84+
Assert.Equal("OIDC_CLIENT_ID", config.ClientId);
85+
Assert.Equal("https://oidc.com/issuer", config.Issuer);
86+
}
87+
88+
[Fact]
89+
[TestRank(20)]
90+
public async Task UpdateProviderConfig()
91+
{
92+
var args = new OidcProviderConfigArgs()
93+
{
94+
ProviderId = this.fixture.ProviderId,
95+
DisplayName = "UPDATED_OIDC_DISPLAY_NAME",
96+
Enabled = false,
97+
ClientId = "UPDATED_OIDC_CLIENT_ID",
98+
Issuer = "https://oidc.com/updated-issuer",
99+
};
100+
101+
var config = await FirebaseAuth.DefaultInstance.UpdateProviderConfigAsync(args);
102+
103+
Assert.Equal(this.fixture.ProviderId, config.ProviderId);
104+
Assert.Equal("UPDATED_OIDC_DISPLAY_NAME", config.DisplayName);
105+
Assert.False(config.Enabled);
106+
Assert.Equal("UPDATED_OIDC_CLIENT_ID", config.ClientId);
107+
Assert.Equal("https://oidc.com/updated-issuer", config.Issuer);
108+
}
109+
110+
[Fact]
111+
[TestRank(30)]
112+
public async Task DeleteProviderConfig()
113+
{
114+
await FirebaseAuth.DefaultInstance.DeleteProviderConfigAsync(this.fixture.ProviderId);
115+
116+
this.fixture.ProviderConfig = null;
117+
118+
var exception = await Assert.ThrowsAsync<FirebaseAuthException>(
119+
() => FirebaseAuth.DefaultInstance.GetOidcProviderConfigAsync(this.fixture.ProviderId));
120+
Assert.Equal(ErrorCode.NotFound, exception.ErrorCode);
121+
Assert.Equal(AuthErrorCode.ConfigurationNotFound, exception.AuthErrorCode);
122+
}
123+
}
124+
125+
/// <summary>
126+
/// A fixture that allows reusing the same <see cref="AuthProviderConfig"/> instance across
127+
/// multiple test cases.
128+
/// </summary>
129+
public abstract class ProviderConfigFixture<T> : IDisposable
130+
where T : AuthProviderConfig
131+
{
132+
public string ProviderId { get; protected set; }
133+
134+
public T ProviderConfig { get; internal set; }
135+
136+
public void Dispose()
137+
{
138+
if (this.ProviderConfig != null)
139+
{
140+
FirebaseAuth.DefaultInstance
141+
.DeleteProviderConfigAsync(this.ProviderConfig.ProviderId)
142+
.Wait();
143+
}
144+
}
145+
146+
protected static string GetRandomIdentifier(int length = 10)
147+
{
148+
var random = new Random();
149+
const string chars = "abcdefghijklmnopqrstuvwxyz0123456789";
150+
var suffix = new string(Enumerable.Repeat(chars, length)
151+
.Select(s => s[random.Next(s.Length)]).ToArray());
152+
return $"id-{suffix}";
153+
}
154+
}
155+
156+
public class OidcProviderConfigFixture : ProviderConfigFixture<OidcProviderConfig>
157+
{
158+
public OidcProviderConfigFixture()
159+
{
160+
IntegrationTestUtils.EnsureDefaultApp();
161+
var providerId = $"oidc.{GetRandomIdentifier()}";
162+
var args = new OidcProviderConfigArgs()
163+
{
164+
ProviderId = providerId,
165+
DisplayName = "OIDC_DISPLAY_NAME",
166+
Enabled = true,
167+
ClientId = "OIDC_CLIENT_ID",
168+
Issuer = "https://oidc.com/issuer",
169+
};
170+
this.ProviderConfig = FirebaseAuth.DefaultInstance
171+
.CreateProviderConfigAsync(args)
172+
.Result;
173+
this.ProviderId = providerId;
174+
}
175+
}
176+
}
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
// Copyright 2020, Google Inc. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using System.Collections.Generic;
16+
using System.Threading.Tasks;
17+
using FirebaseAdmin.Auth;
18+
using FirebaseAdmin.Auth.Providers;
19+
using Xunit;
20+
21+
namespace FirebaseAdmin.IntegrationTests
22+
{
23+
[TestCaseOrderer(
24+
"FirebaseAdmin.IntegrationTests.TestRankOrderer", "FirebaseAdmin.IntegrationTests")]
25+
public class SamlProviderConfigTest : IClassFixture<SamlProviderConfigFixture>
26+
{
27+
private readonly SamlProviderConfigFixture fixture;
28+
29+
public SamlProviderConfigTest(SamlProviderConfigFixture fixture)
30+
{
31+
this.fixture = fixture;
32+
}
33+
34+
[Fact]
35+
[TestRank(0)]
36+
public void CreateProviderConfig()
37+
{
38+
var config = this.fixture.ProviderConfig;
39+
40+
Assert.Equal(this.fixture.ProviderId, config.ProviderId);
41+
Assert.Equal("SAML_DISPLAY_NAME", config.DisplayName);
42+
Assert.True(config.Enabled);
43+
Assert.Equal("IDP_ENTITY_ID", config.IdpEntityId);
44+
Assert.Equal("https://example.com/login", config.SsoUrl);
45+
Assert.Single(config.X509Certificates, SamlProviderConfigFixture.X509Certificates[0]);
46+
Assert.Equal("RP_ENTITY_ID", config.RpEntityId);
47+
Assert.Equal("https://projectId.firebaseapp.com/__/auth/handler", config.CallbackUrl);
48+
}
49+
50+
[Fact]
51+
[TestRank(10)]
52+
public async Task GetProviderConfig()
53+
{
54+
var config = await FirebaseAuth.DefaultInstance.GetSamlProviderConfigAsync(
55+
this.fixture.ProviderId);
56+
57+
Assert.Equal(this.fixture.ProviderId, config.ProviderId);
58+
Assert.Equal("SAML_DISPLAY_NAME", config.DisplayName);
59+
Assert.True(config.Enabled);
60+
Assert.Equal("IDP_ENTITY_ID", config.IdpEntityId);
61+
Assert.Equal("https://example.com/login", config.SsoUrl);
62+
Assert.Single(config.X509Certificates, SamlProviderConfigFixture.X509Certificates[0]);
63+
Assert.Equal("RP_ENTITY_ID", config.RpEntityId);
64+
Assert.Equal("https://projectId.firebaseapp.com/__/auth/handler", config.CallbackUrl);
65+
}
66+
67+
[Fact]
68+
[TestRank(10)]
69+
public async Task ListProviderConfig()
70+
{
71+
SamlProviderConfig config = null;
72+
73+
var pagedEnumerable = FirebaseAuth.DefaultInstance.ListSamlProviderConfigsAsync(null);
74+
var enumerator = pagedEnumerable.GetEnumerator();
75+
while (await enumerator.MoveNext())
76+
{
77+
if (enumerator.Current.ProviderId == this.fixture.ProviderId)
78+
{
79+
config = enumerator.Current;
80+
break;
81+
}
82+
}
83+
84+
Assert.NotNull(config);
85+
Assert.Equal(this.fixture.ProviderId, config.ProviderId);
86+
Assert.Equal("SAML_DISPLAY_NAME", config.DisplayName);
87+
Assert.True(config.Enabled);
88+
Assert.Equal("IDP_ENTITY_ID", config.IdpEntityId);
89+
Assert.Equal("https://example.com/login", config.SsoUrl);
90+
Assert.Single(config.X509Certificates, SamlProviderConfigFixture.X509Certificates[0]);
91+
Assert.Equal("RP_ENTITY_ID", config.RpEntityId);
92+
Assert.Equal("https://projectId.firebaseapp.com/__/auth/handler", config.CallbackUrl);
93+
}
94+
95+
[Fact]
96+
[TestRank(20)]
97+
public async Task UpdateProviderConfig()
98+
{
99+
var args = new SamlProviderConfigArgs()
100+
{
101+
ProviderId = this.fixture.ProviderId,
102+
DisplayName = "UPDATED_SAML_DISPLAY_NAME",
103+
Enabled = false,
104+
IdpEntityId = "UPDATED_IDP_ENTITY_ID",
105+
SsoUrl = "https://example.com/updated-login",
106+
X509Certificates = new List<string>
107+
{
108+
SamlProviderConfigFixture.X509Certificates[1],
109+
},
110+
RpEntityId = "UPDATED_RP_ENTITY_ID",
111+
CallbackUrl = "https://projectId.firebaseapp.com/__/auth/updated-handler",
112+
};
113+
114+
var config = await FirebaseAuth.DefaultInstance.UpdateProviderConfigAsync(args);
115+
116+
Assert.Equal(this.fixture.ProviderId, config.ProviderId);
117+
Assert.Equal("UPDATED_SAML_DISPLAY_NAME", config.DisplayName);
118+
Assert.False(config.Enabled);
119+
Assert.Equal("UPDATED_IDP_ENTITY_ID", config.IdpEntityId);
120+
Assert.Equal("https://example.com/updated-login", config.SsoUrl);
121+
Assert.Single(config.X509Certificates, SamlProviderConfigFixture.X509Certificates[1]);
122+
Assert.Equal("UPDATED_RP_ENTITY_ID", config.RpEntityId);
123+
Assert.Equal(
124+
"https://projectId.firebaseapp.com/__/auth/updated-handler", config.CallbackUrl);
125+
}
126+
127+
[Fact]
128+
[TestRank(30)]
129+
public async Task DeleteProviderConfig()
130+
{
131+
await FirebaseAuth.DefaultInstance.DeleteProviderConfigAsync(this.fixture.ProviderId);
132+
133+
this.fixture.ProviderConfig = null;
134+
135+
var exception = await Assert.ThrowsAsync<FirebaseAuthException>(
136+
() => FirebaseAuth.DefaultInstance.GetSamlProviderConfigAsync(this.fixture.ProviderId));
137+
Assert.Equal(ErrorCode.NotFound, exception.ErrorCode);
138+
Assert.Equal(AuthErrorCode.ConfigurationNotFound, exception.AuthErrorCode);
139+
}
140+
}
141+
142+
public class SamlProviderConfigFixture : ProviderConfigFixture<SamlProviderConfig>
143+
{
144+
internal static readonly IList<string> X509Certificates = new List<string>()
145+
{
146+
"-----BEGIN CERTIFICATE-----\nMIICZjCCAc+gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBQMQswCQYDVQQGEwJ1czEL\nMAkGA1UECAwCQ0ExDTALBgNVBAoMBEFjbWUxETAPBgNVBAMMCGFjbWUuY29tMRIw\nEAYDVQQHDAlTdW5ueXZhbGUwHhcNMTgxMjA2MDc1MTUxWhcNMjgxMjAzMDc1MTUx\nWjBQMQswCQYDVQQGEwJ1czELMAkGA1UECAwCQ0ExDTALBgNVBAoMBEFjbWUxETAP\nBgNVBAMMCGFjbWUuY29tMRIwEAYDVQQHDAlTdW5ueXZhbGUwgZ8wDQYJKoZIhvcN\nAQEBBQADgY0AMIGJAoGBAKphmggjiVgqMLXyzvI7cKphscIIQ+wcv7Dld6MD4aKv\n7Jqr8ltujMxBUeY4LFEKw8Terb01snYpDotfilaG6NxpF/GfVVmMalzwWp0mT8+H\nyzyPj89mRcozu17RwuooR6n1ofXjGcBE86lqC21UhA3WVgjPOLqB42rlE9gPnZLB\nAgMBAAGjUDBOMB0GA1UdDgQWBBS0iM7WnbCNOnieOP1HIA+Oz/ML+zAfBgNVHSME\nGDAWgBS0iM7WnbCNOnieOP1HIA+Oz/ML+zAMBgNVHRMEBTADAQH/MA0GCSqGSIb3\nDQEBDQUAA4GBAF3jBgS+wP+K/jTupEQur6iaqS4UvXd//d4vo1MV06oTLQMTz+rP\nOSMDNwxzfaOn6vgYLKP/Dcy9dSTnSzgxLAxfKvDQZA0vE3udsw0Bd245MmX4+GOp\nlbrN99XP1u+lFxCSdMUzvQ/jW4ysw/Nq4JdJ0gPAyPvL6Qi/3mQdIQwx\n-----END CERTIFICATE-----\n",
147+
"-----BEGIN CERTIFICATE-----\nMIICZjCCAc+gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBQMQswCQYDVQQGEwJ1czEL\nMAkGA1UECAwCQ0ExDTALBgNVBAoMBEFjbWUxETAPBgNVBAMMCGFjbWUuY29tMRIw\nEAYDVQQHDAlTdW5ueXZhbGUwHhcNMTgxMjA2MDc1ODE4WhcNMjgxMjAzMDc1ODE4\nWjBQMQswCQYDVQQGEwJ1czELMAkGA1UECAwCQ0ExDTALBgNVBAoMBEFjbWUxETAP\nBgNVBAMMCGFjbWUuY29tMRIwEAYDVQQHDAlTdW5ueXZhbGUwgZ8wDQYJKoZIhvcN\nAQEBBQADgY0AMIGJAoGBAKuzYKfDZGA6DJgQru3wNUqv+S0hMZfP/jbp8ou/8UKu\nrNeX7cfCgt3yxoGCJYKmF6t5mvo76JY0MWwA53BxeP/oyXmJ93uHG5mFRAsVAUKs\ncVVb0Xi6ujxZGVdDWFV696L0BNOoHTfXmac6IBoZQzNNK4n1AATqwo+z7a0pfRrJ\nAgMBAAGjUDBOMB0GA1UdDgQWBBSKmi/ZKMuLN0ES7/jPa7q7jAjPiDAfBgNVHSME\nGDAWgBSKmi/ZKMuLN0ES7/jPa7q7jAjPiDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3\nDQEBDQUAA4GBAAg2a2kSn05NiUOuWOHwPUjW3wQRsGxPXtbhWMhmNdCfKKteM2+/\nLd/jz5F3qkOgGQ3UDgr3SHEoWhnLaJMF4a2tm6vL2rEIfPEK81KhTTRxSsAgMVbU\nJXBz1md6Ur0HlgQC7d1CHC8/xi2DDwHopLyxhogaZUxy9IaRxUEa2vJW\n-----END CERTIFICATE-----\n",
148+
};
149+
150+
public SamlProviderConfigFixture()
151+
{
152+
IntegrationTestUtils.EnsureDefaultApp();
153+
var providerId = $"saml.{GetRandomIdentifier()}";
154+
var args = new SamlProviderConfigArgs()
155+
{
156+
ProviderId = providerId,
157+
DisplayName = "SAML_DISPLAY_NAME",
158+
Enabled = true,
159+
IdpEntityId = "IDP_ENTITY_ID",
160+
SsoUrl = "https://example.com/login",
161+
X509Certificates = new List<string> { X509Certificates[0] },
162+
RpEntityId = "RP_ENTITY_ID",
163+
CallbackUrl = "https://projectId.firebaseapp.com/__/auth/handler",
164+
};
165+
this.ProviderConfig = FirebaseAuth.DefaultInstance
166+
.CreateProviderConfigAsync(args)
167+
.Result;
168+
this.ProviderId = providerId;
169+
}
170+
}
171+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2020, Google Inc. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using System;
16+
17+
namespace FirebaseAdmin.IntegrationTests
18+
{
19+
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
20+
public class TestRankAttribute : Attribute
21+
{
22+
public TestRankAttribute(int rank) => this.Rank = rank;
23+
24+
internal int Rank { get; private set; }
25+
}
26+
}

0 commit comments

Comments
 (0)