Skip to content

Commit 7ece40c

Browse files
authored
chore: Adding integration tests for multi-tenancy support (#238)
* chore: Added integration tests for multi-tenancy APIs * fix: Refactored TenantFixture to handle init all test resources
1 parent 17bc8a4 commit 7ece40c

File tree

7 files changed

+743
-130
lines changed

7 files changed

+743
-130
lines changed
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
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.Net.Http;
18+
using System.Text;
19+
using System.Threading.Tasks;
20+
using Google.Apis.Http;
21+
using Google.Apis.Json;
22+
using Google.Apis.Requests;
23+
using Google.Apis.Util;
24+
using Newtonsoft.Json;
25+
26+
namespace FirebaseAdmin.IntegrationTests.Auth
27+
{
28+
internal class AuthIntegrationUtils
29+
{
30+
internal static readonly NewtonsoftJsonSerializer JsonParser =
31+
NewtonsoftJsonSerializer.Instance;
32+
33+
private const string EmailLinkSignInUrl =
34+
"https://www.googleapis.com/identitytoolkit/v3/relyingparty/emailLinkSignin";
35+
36+
private const string ResetPasswordUrl =
37+
"https://www.googleapis.com/identitytoolkit/v3/relyingparty/resetPassword";
38+
39+
private const string VerifyCustomTokenUrl =
40+
"https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyCustomToken";
41+
42+
internal static async Task<string> SignInWithCustomTokenAsync(
43+
string customToken, string tenantId = null)
44+
{
45+
var rb = new RequestBuilder()
46+
{
47+
Method = HttpConsts.Post,
48+
BaseUri = new Uri(VerifyCustomTokenUrl),
49+
};
50+
rb.AddParameter(RequestParameterType.Query, "key", IntegrationTestUtils.GetApiKey());
51+
52+
var request = rb.CreateRequest();
53+
var payload = JsonParser.Serialize(new SignInRequest
54+
{
55+
CustomToken = customToken,
56+
TenantId = tenantId,
57+
ReturnSecureToken = true,
58+
});
59+
request.Content = new StringContent(payload, Encoding.UTF8, "application/json");
60+
61+
var response = await SendAndDeserialize<SignInResponse>(request);
62+
return response.IdToken;
63+
}
64+
65+
internal static async Task<string> ResetPasswordAsync(ResetPasswordRequest data)
66+
{
67+
var rb = new RequestBuilder()
68+
{
69+
Method = HttpConsts.Post,
70+
BaseUri = new Uri(ResetPasswordUrl),
71+
};
72+
rb.AddParameter(RequestParameterType.Query, "key", IntegrationTestUtils.GetApiKey());
73+
74+
var payload = JsonParser.Serialize(data);
75+
var request = rb.CreateRequest();
76+
request.Content = new StringContent(payload, Encoding.UTF8, "application/json");
77+
78+
var response = await SendAndDeserialize<Dictionary<string, object>>(request);
79+
return (string)response["email"];
80+
}
81+
82+
internal static async Task<string> SignInWithEmailLinkAsync(
83+
string email, string oobCode, string tenantId = null)
84+
{
85+
var rb = new RequestBuilder()
86+
{
87+
Method = HttpConsts.Post,
88+
BaseUri = new Uri(EmailLinkSignInUrl),
89+
};
90+
rb.AddParameter(RequestParameterType.Query, "key", IntegrationTestUtils.GetApiKey());
91+
92+
var data = new Dictionary<string, object>()
93+
{
94+
{ "email", email },
95+
{ "oobCode", oobCode },
96+
};
97+
if (tenantId != null)
98+
{
99+
data["tenantId"] = tenantId;
100+
}
101+
102+
var payload = JsonParser.Serialize(data);
103+
var request = rb.CreateRequest();
104+
request.Content = new StringContent(payload, Encoding.UTF8, "application/json");
105+
106+
var response = await SendAndDeserialize<Dictionary<string, object>>(request);
107+
return (string)response["idToken"];
108+
}
109+
110+
private static async Task<T> SendAndDeserialize<T>(HttpRequestMessage request)
111+
{
112+
using (var client = new HttpClient())
113+
{
114+
var response = await client.SendAsync(request);
115+
response.EnsureSuccessStatusCode();
116+
var json = await response.Content.ReadAsStringAsync();
117+
return JsonParser.Deserialize<T>(json);
118+
}
119+
}
120+
121+
internal class SignInRequest
122+
{
123+
[JsonProperty("token")]
124+
public string CustomToken { get; set; }
125+
126+
[JsonProperty("tenantId")]
127+
public string TenantId { get; set; }
128+
129+
[JsonProperty("returnSecureToken")]
130+
public bool ReturnSecureToken { get; set; }
131+
}
132+
133+
internal class SignInResponse
134+
{
135+
[JsonProperty("idToken")]
136+
public string IdToken { get; set; }
137+
}
138+
}
139+
140+
internal class ResetPasswordRequest
141+
{
142+
[JsonProperty("email")]
143+
public string Email { get; set; }
144+
145+
[JsonProperty("oldPassword")]
146+
public string OldPassword { get; set; }
147+
148+
[JsonProperty("newPassword")]
149+
public string NewPassword { get; set; }
150+
151+
[JsonProperty("oobCode")]
152+
public string OobCode { get; set; }
153+
154+
[JsonProperty("tenantId")]
155+
public string TenantId { get; set; }
156+
}
157+
}

FirebaseAdmin/FirebaseAdmin.IntegrationTests/Auth/FirebaseAuthTest.cs

Lines changed: 10 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,6 @@ namespace FirebaseAdmin.IntegrationTests.Auth
2929
{
3030
public class FirebaseAuthTest : IClassFixture<TemporaryUserBuilder>
3131
{
32-
private const string EmailLinkSignInUrl =
33-
"https://www.googleapis.com/identitytoolkit/v3/relyingparty/emailLinkSignin";
34-
35-
private const string ResetPasswordUrl =
36-
"https://www.googleapis.com/identitytoolkit/v3/relyingparty/resetPassword";
37-
38-
private const string VerifyCustomTokenUrl =
39-
"https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyCustomToken";
40-
4132
private const string VerifyPasswordUrl =
4233
"https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword";
4334

@@ -61,7 +52,7 @@ public async Task CreateCustomToken()
6152
{
6253
var customToken = await FirebaseAuth.DefaultInstance
6354
.CreateCustomTokenAsync("testuser");
64-
var idToken = await SignInWithCustomTokenAsync(customToken);
55+
var idToken = await AuthIntegrationUtils.SignInWithCustomTokenAsync(customToken);
6556
var decoded = await FirebaseAuth.DefaultInstance.VerifyIdTokenAsync(idToken);
6657
Assert.Equal("testuser", decoded.Uid);
6758
}
@@ -77,7 +68,7 @@ public async Task CreateCustomTokenWithClaims()
7768
};
7869
var customToken = await FirebaseAuth.DefaultInstance.CreateCustomTokenAsync(
7970
"testuser", developerClaims);
80-
var idToken = await SignInWithCustomTokenAsync(customToken);
71+
var idToken = await AuthIntegrationUtils.SignInWithCustomTokenAsync(customToken);
8172
Assert.False(string.IsNullOrEmpty(idToken));
8273
var decoded = await FirebaseAuth.DefaultInstance.VerifyIdTokenAsync(idToken);
8374
Assert.Equal("testuser", decoded.Uid);
@@ -105,7 +96,7 @@ public async Task CreateCustomTokenWithoutServiceAccount()
10596
{
10697
var customToken = await FirebaseAuth.GetAuth(app).CreateCustomTokenAsync(
10798
"testuser");
108-
var idToken = await SignInWithCustomTokenAsync(customToken);
99+
var idToken = await AuthIntegrationUtils.SignInWithCustomTokenAsync(customToken);
109100
var decoded = await FirebaseAuth.DefaultInstance.VerifyIdTokenAsync(idToken);
110101
Assert.Equal("testuser", decoded.Uid);
111102
}
@@ -120,7 +111,7 @@ public async Task RevokeRefreshTokens()
120111
{
121112
var customToken = await FirebaseAuth.DefaultInstance
122113
.CreateCustomTokenAsync("testuser");
123-
var idToken = await SignInWithCustomTokenAsync(customToken);
114+
var idToken = await AuthIntegrationUtils.SignInWithCustomTokenAsync(customToken);
124115
var decoded = await FirebaseAuth.DefaultInstance.VerifyIdTokenAsync(idToken, true);
125116
Assert.Equal("testuser", decoded.Uid);
126117

@@ -135,7 +126,7 @@ public async Task RevokeRefreshTokens()
135126
Assert.Equal(ErrorCode.InvalidArgument, exception.ErrorCode);
136127
Assert.Equal(AuthErrorCode.RevokedIdToken, exception.AuthErrorCode);
137128

138-
idToken = await SignInWithCustomTokenAsync(customToken);
129+
idToken = await AuthIntegrationUtils.SignInWithCustomTokenAsync(customToken);
139130
decoded = await FirebaseAuth.DefaultInstance.VerifyIdTokenAsync(idToken, true);
140131
Assert.Equal("testuser", decoded.Uid);
141132
}
@@ -598,7 +589,7 @@ public async Task PasswordResetLink()
598589
NewPassword = "NewP@$$w0rd",
599590
OobCode = query["oobCode"],
600591
};
601-
var resetEmail = await ResetPasswordAsync(request);
592+
var resetEmail = await AuthIntegrationUtils.ResetPasswordAsync(request);
602593
Assert.Equal(user.Email, resetEmail);
603594

604595
// Password reset also verifies the user's email
@@ -618,7 +609,8 @@ public async Task SignInWithEmailLink()
618609
var query = HttpUtility.ParseQueryString(uri.Query);
619610
Assert.Equal(ContinueUrl, query["continueUrl"]);
620611

621-
var idToken = await SignInWithEmailLinkAsync(user.Email, query["oobCode"]);
612+
var idToken = await AuthIntegrationUtils.SignInWithEmailLinkAsync(
613+
user.Email, query["oobCode"]);
622614
Assert.NotEmpty(idToken);
623615

624616
// Sign in with link also verifies the user's email
@@ -630,7 +622,7 @@ public async Task SignInWithEmailLink()
630622
public async Task SessionCookie()
631623
{
632624
var customToken = await FirebaseAuth.DefaultInstance.CreateCustomTokenAsync("testuser");
633-
var idToken = await SignInWithCustomTokenAsync(customToken);
625+
var idToken = await AuthIntegrationUtils.SignInWithCustomTokenAsync(customToken);
634626

635627
var options = new SessionCookieOptions()
636628
{
@@ -652,39 +644,13 @@ public async Task SessionCookie()
652644
Assert.Equal(ErrorCode.InvalidArgument, exception.ErrorCode);
653645
Assert.Equal(AuthErrorCode.RevokedSessionCookie, exception.AuthErrorCode);
654646

655-
idToken = await SignInWithCustomTokenAsync(customToken);
647+
idToken = await AuthIntegrationUtils.SignInWithCustomTokenAsync(customToken);
656648
sessionCookie = await FirebaseAuth.DefaultInstance.CreateSessionCookieAsync(
657649
idToken, options);
658650
decoded = await FirebaseAuth.DefaultInstance.VerifySessionCookieAsync(sessionCookie, true);
659651
Assert.Equal("testuser", decoded.Uid);
660652
}
661653

662-
private static async Task<string> SignInWithCustomTokenAsync(string customToken)
663-
{
664-
var rb = new Google.Apis.Requests.RequestBuilder()
665-
{
666-
Method = Google.Apis.Http.HttpConsts.Post,
667-
BaseUri = new Uri(VerifyCustomTokenUrl),
668-
};
669-
rb.AddParameter(RequestParameterType.Query, "key", IntegrationTestUtils.GetApiKey());
670-
var request = rb.CreateRequest();
671-
var jsonSerializer = Google.Apis.Json.NewtonsoftJsonSerializer.Instance;
672-
var payload = jsonSerializer.Serialize(new SignInRequest
673-
{
674-
CustomToken = customToken,
675-
ReturnSecureToken = true,
676-
});
677-
request.Content = new StringContent(payload, Encoding.UTF8, "application/json");
678-
using (var client = new HttpClient())
679-
{
680-
var response = await client.SendAsync(request);
681-
response.EnsureSuccessStatusCode();
682-
var json = await response.Content.ReadAsStringAsync();
683-
var parsed = jsonSerializer.Deserialize<SignInResponse>(json);
684-
return parsed.IdToken;
685-
}
686-
}
687-
688654
private static async Task<string> SignInWithPasswordAsync(string email, string password)
689655
{
690656
var rb = new Google.Apis.Requests.RequestBuilder()
@@ -712,59 +678,6 @@ private static async Task<string> SignInWithPasswordAsync(string email, string p
712678
}
713679
}
714680

715-
private static async Task<string> SignInWithEmailLinkAsync(string email, string oobCode)
716-
{
717-
var rb = new Google.Apis.Requests.RequestBuilder()
718-
{
719-
Method = Google.Apis.Http.HttpConsts.Post,
720-
BaseUri = new Uri(EmailLinkSignInUrl),
721-
};
722-
rb.AddParameter(RequestParameterType.Query, "key", IntegrationTestUtils.GetApiKey());
723-
724-
var data = new Dictionary<string, object>()
725-
{
726-
{ "email", email },
727-
{ "oobCode", oobCode },
728-
};
729-
var jsonSerializer = Google.Apis.Json.NewtonsoftJsonSerializer.Instance;
730-
var payload = jsonSerializer.Serialize(data);
731-
732-
var request = rb.CreateRequest();
733-
request.Content = new StringContent(payload, Encoding.UTF8, "application/json");
734-
using (var client = new HttpClient())
735-
{
736-
var response = await client.SendAsync(request);
737-
response.EnsureSuccessStatusCode();
738-
var json = await response.Content.ReadAsStringAsync();
739-
var parsed = jsonSerializer.Deserialize<Dictionary<string, object>>(json);
740-
return (string)parsed["idToken"];
741-
}
742-
}
743-
744-
private static async Task<string> ResetPasswordAsync(ResetPasswordRequest data)
745-
{
746-
var rb = new Google.Apis.Requests.RequestBuilder()
747-
{
748-
Method = Google.Apis.Http.HttpConsts.Post,
749-
BaseUri = new Uri(ResetPasswordUrl),
750-
};
751-
rb.AddParameter(RequestParameterType.Query, "key", IntegrationTestUtils.GetApiKey());
752-
753-
var jsonSerializer = Google.Apis.Json.NewtonsoftJsonSerializer.Instance;
754-
var payload = jsonSerializer.Serialize(data);
755-
756-
var request = rb.CreateRequest();
757-
request.Content = new StringContent(payload, Encoding.UTF8, "application/json");
758-
using (var client = new HttpClient())
759-
{
760-
var response = await client.SendAsync(request);
761-
response.EnsureSuccessStatusCode();
762-
var json = await response.Content.ReadAsStringAsync();
763-
var parsed = jsonSerializer.Deserialize<Dictionary<string, object>>(json);
764-
return (string)parsed["email"];
765-
}
766-
}
767-
768681
/**
769682
* The <c>batchDelete</c> endpoint is currently rate limited to 1qps. Use this test helper
770683
* to ensure you don't run into quota exceeded errors.
@@ -791,36 +704,6 @@ internal static void NotNull(object obj, string msg)
791704
}
792705
}
793706

794-
internal class ResetPasswordRequest
795-
{
796-
[Newtonsoft.Json.JsonProperty("email")]
797-
public string Email { get; set; }
798-
799-
[Newtonsoft.Json.JsonProperty("oldPassword")]
800-
public string OldPassword { get; set; }
801-
802-
[Newtonsoft.Json.JsonProperty("newPassword")]
803-
public string NewPassword { get; set; }
804-
805-
[Newtonsoft.Json.JsonProperty("oobCode")]
806-
public string OobCode { get; set; }
807-
}
808-
809-
internal class SignInRequest
810-
{
811-
[Newtonsoft.Json.JsonProperty("token")]
812-
public string CustomToken { get; set; }
813-
814-
[Newtonsoft.Json.JsonProperty("returnSecureToken")]
815-
public bool ReturnSecureToken { get; set; }
816-
}
817-
818-
internal class SignInResponse
819-
{
820-
[Newtonsoft.Json.JsonProperty("idToken")]
821-
public string IdToken { get; set; }
822-
}
823-
824707
internal class VerifyPasswordRequest
825708
{
826709
[Newtonsoft.Json.JsonProperty("email")]

0 commit comments

Comments
 (0)