diff --git a/.travis.yml b/.travis.yml
index 06088137..cf183702 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,4 +8,5 @@ matrix:
script:
- dotnet build FirebaseAdmin/FirebaseAdmin
- dotnet build FirebaseAdmin/FirebaseAdmin.Snippets
+ - dotnet build FirebaseAdmin/FirebaseAdmin.IntegrationTests
- dotnet test FirebaseAdmin/FirebaseAdmin.Tests
diff --git a/FirebaseAdmin/FirebaseAdmin.IntegrationTests/FirebaseAdmin.IntegrationTests.csproj b/FirebaseAdmin/FirebaseAdmin.IntegrationTests/FirebaseAdmin.IntegrationTests.csproj
index c428e7b4..d146599a 100644
--- a/FirebaseAdmin/FirebaseAdmin.IntegrationTests/FirebaseAdmin.IntegrationTests.csproj
+++ b/FirebaseAdmin/FirebaseAdmin.IntegrationTests/FirebaseAdmin.IntegrationTests.csproj
@@ -2,8 +2,9 @@
netcoreapp2.0
-
false
+ true
+ ../../stylecop_test.ruleset
@@ -12,6 +13,9 @@
+
+ all
+
diff --git a/FirebaseAdmin/FirebaseAdmin.IntegrationTests/FirebaseAuthTest.cs b/FirebaseAdmin/FirebaseAdmin.IntegrationTests/FirebaseAuthTest.cs
index 1c56baf5..af4d0dce 100644
--- a/FirebaseAdmin/FirebaseAdmin.IntegrationTests/FirebaseAuthTest.cs
+++ b/FirebaseAdmin/FirebaseAdmin.IntegrationTests/FirebaseAuthTest.cs
@@ -17,24 +17,24 @@
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
-using Xunit;
using FirebaseAdmin;
using FirebaseAdmin.Auth;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Util;
+using Xunit;
namespace FirebaseAdmin.IntegrationTests
{
public class FirebaseAuthTest
{
- private const string VerifyCustomTokenUrl =
+ private const string VerifyCustomTokenUrl =
"https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyCustomToken";
public FirebaseAuthTest()
{
IntegrationTestUtils.EnsureDefaultApp();
}
-
+
[Fact]
public async Task CreateCustomToken()
{
@@ -50,9 +50,9 @@ public async Task CreateCustomTokenWithClaims()
{
var developerClaims = new Dictionary()
{
- {"admin", true},
- {"package", "gold"},
- {"magicNumber", 42L},
+ { "admin", true },
+ { "package", "gold" },
+ { "magicNumber", 42L },
};
var customToken = await FirebaseAuth.DefaultInstance.CreateCustomTokenAsync(
"testuser", developerClaims);
@@ -72,13 +72,14 @@ public async Task CreateCustomTokenWithClaims()
public async Task CreateCustomTokenWithoutServiceAccount()
{
var googleCred = FirebaseApp.DefaultInstance.Options.Credential;
- var serviceAcct = (ServiceAccountCredential) googleCred.UnderlyingCredential;
- var token = await ((ITokenAccess) googleCred).GetAccessTokenForRequestAsync();
- var app = FirebaseApp.Create(new AppOptions()
- {
- Credential = GoogleCredential.FromAccessToken(token),
- ServiceAccountId = serviceAcct.Id,
- }, "IAMSignApp");
+ var serviceAcct = (ServiceAccountCredential)googleCred.UnderlyingCredential;
+ var token = await ((ITokenAccess)googleCred).GetAccessTokenForRequestAsync();
+ var app = FirebaseApp.Create(
+ new AppOptions()
+ {
+ Credential = GoogleCredential.FromAccessToken(token),
+ ServiceAccountId = serviceAcct.Id,
+ }, "IAMSignApp");
try
{
var customToken = await FirebaseAuth.GetAuth(app).CreateCustomTokenAsync(
@@ -98,7 +99,7 @@ public async Task SetCustomUserClaims()
{
var customClaims = new Dictionary()
{
- {"admin", true}
+ { "admin", true },
};
await FirebaseAuth.DefaultInstance.SetCustomUserClaimsAsync("testuser", customClaims);
@@ -126,12 +127,13 @@ private static async Task SignInWithCustomTokenAsync(string customToken)
var rb = new Google.Apis.Requests.RequestBuilder()
{
Method = Google.Apis.Http.HttpConsts.Post,
- BaseUri = new Uri(VerifyCustomTokenUrl),
+ BaseUri = new Uri(VerifyCustomTokenUrl),
};
rb.AddParameter(RequestParameterType.Query, "key", IntegrationTestUtils.GetApiKey());
var request = rb.CreateRequest();
var jsonSerializer = Google.Apis.Json.NewtonsoftJsonSerializer.Instance;
- var payload = jsonSerializer.Serialize(new SignInRequest{
+ var payload = jsonSerializer.Serialize(new SignInRequest
+ {
CustomToken = customToken,
ReturnSecureToken = true,
});
@@ -159,6 +161,6 @@ internal class SignInRequest
internal class SignInResponse
{
[Newtonsoft.Json.JsonProperty("idToken")]
- public String IdToken { get; set; }
+ public string IdToken { get; set; }
}
}
diff --git a/FirebaseAdmin/FirebaseAdmin.IntegrationTests/IntegrationTestUtils.cs b/FirebaseAdmin/FirebaseAdmin.IntegrationTests/IntegrationTestUtils.cs
index 5761de85..bee0adc0 100644
--- a/FirebaseAdmin/FirebaseAdmin.IntegrationTests/IntegrationTestUtils.cs
+++ b/FirebaseAdmin/FirebaseAdmin.IntegrationTests/IntegrationTestUtils.cs
@@ -26,13 +26,15 @@ internal static class IntegrationTestUtils
private const string ServiceAccountFile = "./resources/integration_cert.json";
private const string ApiKeyFile = "./resources/integration_apikey.txt";
- private static readonly Lazy DefaultFirebaseApp = new Lazy(() => {
- var options = new AppOptions()
+ private static readonly Lazy DefaultFirebaseApp = new Lazy(
+ () =>
{
- Credential = GoogleCredential.FromFile(ServiceAccountFile),
- };
- return FirebaseApp.Create(options);
- }, true);
+ var options = new AppOptions()
+ {
+ Credential = GoogleCredential.FromFile(ServiceAccountFile),
+ };
+ return FirebaseApp.Create(options);
+ }, true);
public static FirebaseApp EnsureDefaultApp()
{
diff --git a/FirebaseAdmin/FirebaseAdmin.Snippets/FirebaseAdmin.Snippets.csproj b/FirebaseAdmin/FirebaseAdmin.Snippets/FirebaseAdmin.Snippets.csproj
index 2a896f89..ee80a864 100644
--- a/FirebaseAdmin/FirebaseAdmin.Snippets/FirebaseAdmin.Snippets.csproj
+++ b/FirebaseAdmin/FirebaseAdmin.Snippets/FirebaseAdmin.Snippets.csproj
@@ -3,6 +3,8 @@
netcoreapp2.0
false
+ true
+ ../../stylecop_test.ruleset
@@ -11,6 +13,9 @@
+
+ all
+
diff --git a/FirebaseAdmin/FirebaseAdmin.Snippets/FirebaseAppSnippets.cs b/FirebaseAdmin/FirebaseAdmin.Snippets/FirebaseAppSnippets.cs
index 106daa1a..32ca9be5 100644
--- a/FirebaseAdmin/FirebaseAdmin.Snippets/FirebaseAppSnippets.cs
+++ b/FirebaseAdmin/FirebaseAdmin.Snippets/FirebaseAppSnippets.cs
@@ -21,9 +21,9 @@
namespace FirebaseAdmin.Snippets
{
- class FirebaseAppSnippets
+ internal class FirebaseAppSnippets
{
- static void InitSdkWithServiceAccount()
+ internal static void InitSdkWithServiceAccount()
{
// [START initialize_sdk_with_service_account]
FirebaseApp.Create(new AppOptions()
@@ -33,7 +33,7 @@ static void InitSdkWithServiceAccount()
// [END initialize_sdk_with_service_account]
}
- static void InitSdkWithApplicationDefault()
+ internal static void InitSdkWithApplicationDefault()
{
// [START initialize_sdk_with_application_default]
FirebaseApp.Create(new AppOptions()
@@ -43,7 +43,7 @@ static void InitSdkWithApplicationDefault()
// [END initialize_sdk_with_application_default]
}
- static void InitSdkWithRefreshToken()
+ internal static void InitSdkWithRefreshToken()
{
// [START initialize_sdk_with_refresh_token]
FirebaseApp.Create(new AppOptions()
@@ -53,14 +53,14 @@ static void InitSdkWithRefreshToken()
// [END initialize_sdk_with_refresh_token]
}
- static void InitSdkWithDefaultConfig()
+ internal static void InitSdkWithDefaultConfig()
{
// [START initialize_sdk_with_default_config]
FirebaseApp.Create();
// [END initialize_sdk_with_default_config]
}
- static void InitDefaultApp()
+ internal static void InitDefaultApp()
{
// [START access_services_default]
// Initialize the default app
@@ -78,7 +78,7 @@ static void InitDefaultApp()
// [END access_services_default]
}
- static void InitCustomApp()
+ internal static void InitCustomApp()
{
var defaultOptions = new AppOptions()
{
@@ -107,7 +107,7 @@ static void InitCustomApp()
// [END access_services_nondefault]
}
- static void InitWithServiceAccountId()
+ internal static void InitWithServiceAccountId()
{
// [START initialize_sdk_with_service_account_id]
FirebaseApp.Create(new AppOptions()
diff --git a/FirebaseAdmin/FirebaseAdmin.Snippets/FirebaseAuthSnippets.cs b/FirebaseAdmin/FirebaseAdmin.Snippets/FirebaseAuthSnippets.cs
index bb7e094e..0afb71e2 100644
--- a/FirebaseAdmin/FirebaseAdmin.Snippets/FirebaseAuthSnippets.cs
+++ b/FirebaseAdmin/FirebaseAdmin.Snippets/FirebaseAuthSnippets.cs
@@ -19,9 +19,9 @@
namespace FirebaseAdmin.Snippets
{
- class FirebaseAuthSnippets
+ internal class FirebaseAuthSnippets
{
- static async Task CreateCustomTokenAsync()
+ internal static async Task CreateCustomTokenAsync()
{
// [START custom_token]
var uid = "some-uid";
@@ -32,7 +32,7 @@ static async Task CreateCustomTokenAsync()
Console.WriteLine("Created custom token: {0}", customToken);
}
- static async Task CreateCustomTokenWithClaimsAsync()
+ internal static async Task CreateCustomTokenWithClaimsAsync()
{
// [START custom_token_with_claims]
var uid = "some-uid";
@@ -48,7 +48,7 @@ static async Task CreateCustomTokenWithClaimsAsync()
Console.WriteLine("Created custom token: {0}", customToken);
}
- static async Task VeridyIdTokenAsync(string idToken)
+ internal static async Task VeridyIdTokenAsync(string idToken)
{
// [START verify_id_token]
FirebaseToken decodedToken = await FirebaseAuth.DefaultInstance
diff --git a/FirebaseAdmin/FirebaseAdmin.Tests/Auth/FirebaseAuthTest.cs b/FirebaseAdmin/FirebaseAdmin.Tests/Auth/FirebaseAuthTest.cs
index bc8a90df..491aa9af 100644
--- a/FirebaseAdmin/FirebaseAdmin.Tests/Auth/FirebaseAuthTest.cs
+++ b/FirebaseAdmin/FirebaseAdmin.Tests/Auth/FirebaseAuthTest.cs
@@ -20,15 +20,17 @@
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using Xunit;
+using FirebaseAdmin.Auth;
+using Google.Apis.Auth;
using Google.Apis.Auth.OAuth2;
+using Xunit;
[assembly: CollectionBehavior(DisableTestParallelization = true)]
namespace FirebaseAdmin.Auth.Tests
{
- public class FirebaseAuthTest: IDisposable
+ public class FirebaseAuthTest : IDisposable
{
- private static readonly GoogleCredential mockCredential =
+ private static readonly GoogleCredential MockCredential =
GoogleCredential.FromAccessToken("test-token");
[Fact]
@@ -40,7 +42,7 @@ public void GetAuthWithoutApp()
[Fact]
public void GetDefaultAuth()
{
- var app = FirebaseApp.Create(new AppOptions(){Credential = mockCredential});
+ var app = FirebaseApp.Create(new AppOptions() { Credential = MockCredential });
FirebaseAuth auth = FirebaseAuth.DefaultInstance;
Assert.Same(auth, FirebaseAuth.DefaultInstance);
app.Delete();
@@ -50,7 +52,7 @@ public void GetDefaultAuth()
[Fact]
public void GetAuth()
{
- var app = FirebaseApp.Create(new AppOptions(){Credential = mockCredential}, "MyApp");
+ var app = FirebaseApp.Create(new AppOptions() { Credential = MockCredential }, "MyApp");
FirebaseAuth auth = FirebaseAuth.GetAuth(app);
Assert.Same(auth, FirebaseAuth.GetAuth(app));
app.Delete();
@@ -60,7 +62,7 @@ public void GetAuth()
[Fact]
public async Task UseAfterDelete()
{
- var app = FirebaseApp.Create(new AppOptions(){Credential = mockCredential});
+ var app = FirebaseApp.Create(new AppOptions() { Credential = MockCredential });
FirebaseAuth auth = FirebaseAuth.DefaultInstance;
app.Delete();
await Assert.ThrowsAsync(
@@ -73,7 +75,7 @@ await Assert.ThrowsAsync(
public async Task CreateCustomToken()
{
var cred = GoogleCredential.FromFile("./resources/service_account.json");
- FirebaseApp.Create(new AppOptions(){Credential = cred});
+ FirebaseApp.Create(new AppOptions() { Credential = cred });
var token = await FirebaseAuth.DefaultInstance.CreateCustomTokenAsync("user1");
VerifyCustomToken(token, "user1", null);
}
@@ -82,12 +84,12 @@ public async Task CreateCustomToken()
public async Task CreateCustomTokenWithClaims()
{
var cred = GoogleCredential.FromFile("./resources/service_account.json");
- FirebaseApp.Create(new AppOptions(){Credential = cred});
+ FirebaseApp.Create(new AppOptions() { Credential = cred });
var developerClaims = new Dictionary()
{
- {"admin", true},
- {"package", "gold"},
- {"magicNumber", 42L},
+ { "admin", true },
+ { "package", "gold" },
+ { "magicNumber", 42L },
};
var token = await FirebaseAuth.DefaultInstance.CreateCustomTokenAsync(
"user2", developerClaims);
@@ -98,7 +100,7 @@ public async Task CreateCustomTokenWithClaims()
public async Task CreateCustomTokenCancel()
{
var cred = GoogleCredential.FromFile("./resources/service_account.json");
- FirebaseApp.Create(new AppOptions(){Credential = cred});
+ FirebaseApp.Create(new AppOptions() { Credential = cred });
var canceller = new CancellationTokenSource();
canceller.Cancel();
await Assert.ThrowsAsync(
@@ -109,7 +111,7 @@ await Assert.ThrowsAsync(
[Fact]
public async Task CreateCustomTokenInvalidCredential()
{
- FirebaseApp.Create(new AppOptions(){Credential = mockCredential});
+ FirebaseApp.Create(new AppOptions() { Credential = MockCredential });
await Assert.ThrowsAsync(
async () => await FirebaseAuth.DefaultInstance.CreateCustomTokenAsync("user1"));
}
@@ -117,7 +119,7 @@ await Assert.ThrowsAsync(
[Fact]
public async Task VerifyIdTokenNoProjectId()
{
- FirebaseApp.Create(new AppOptions(){Credential = mockCredential});
+ FirebaseApp.Create(new AppOptions() { Credential = MockCredential });
var idToken = await FirebaseTokenVerifierTest.CreateTestTokenAsync();
await Assert.ThrowsAsync(
async () => await FirebaseAuth.DefaultInstance.VerifyIdTokenAsync(idToken));
@@ -128,7 +130,7 @@ public async Task VerifyIdTokenCancel()
{
FirebaseApp.Create(new AppOptions()
{
- Credential = mockCredential,
+ Credential = MockCredential,
ProjectId = "test-project",
});
var canceller = new CancellationTokenSource();
@@ -139,12 +141,29 @@ await Assert.ThrowsAnyAsync(
idToken, canceller.Token));
}
+ [Fact]
+ public async Task SetCustomUserClaimsNoProjectId()
+ {
+ FirebaseApp.Create(new AppOptions() { Credential = MockCredential });
+ var customClaims = new Dictionary()
+ {
+ { "admin", true },
+ };
+ await Assert.ThrowsAsync(
+ async () => await FirebaseAuth.DefaultInstance.SetCustomUserClaimsAsync("user1", customClaims));
+ }
+
+ public void Dispose()
+ {
+ FirebaseApp.DeleteAll();
+ }
+
private static void VerifyCustomToken(string token, string uid, Dictionary claims)
{
- String[] segments = token.Split(".");
+ string[] segments = token.Split(".");
Assert.Equal(3, segments.Length);
- var payload = JwtUtils.Decode(segments[1]);
+ var payload = JwtUtils.Decode(segments[1]);
Assert.Equal("client@test-project.iam.gserviceaccount.com", payload.Issuer);
Assert.Equal("client@test-project.iam.gserviceaccount.com", payload.Subject);
Assert.Equal(uid, payload.Uid);
@@ -164,28 +183,11 @@ private static void VerifyCustomToken(string token, string uid, Dictionary()
- {
- {"admin", true}
- };
- await Assert.ThrowsAsync(
- async () => await FirebaseAuth.DefaultInstance.SetCustomUserClaimsAsync("user1", customClaims));
- }
-
- public void Dispose()
- {
- FirebaseApp.DeleteAll();
- }
}
}
diff --git a/FirebaseAdmin/FirebaseAdmin.Tests/Auth/FirebaseTokenFactoryTest.cs b/FirebaseAdmin/FirebaseAdmin.Tests/Auth/FirebaseTokenFactoryTest.cs
index 6ad7d462..fa954b82 100644
--- a/FirebaseAdmin/FirebaseAdmin.Tests/Auth/FirebaseTokenFactoryTest.cs
+++ b/FirebaseAdmin/FirebaseAdmin.Tests/Auth/FirebaseTokenFactoryTest.cs
@@ -20,11 +20,11 @@
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using Xunit;
-using FirebaseAdmin.Tests;
using FirebaseAdmin.Auth;
+using FirebaseAdmin.Tests;
using Google.Apis.Auth;
using Google.Apis.Util;
+using Xunit;
namespace FirebaseAdmin.Auth.Tests
{
@@ -56,9 +56,9 @@ public async Task CreateCustomTokenWithClaims()
var factory = new FirebaseTokenFactory(new MockSigner(), clock);
var developerClaims = new Dictionary()
{
- {"admin", true},
- {"package", "gold"},
- {"magicNumber", 42L},
+ { "admin", true },
+ { "package", "gold" },
+ { "magicNumber", 42L },
};
var token = await factory.CreateCustomTokenAsync("user2", developerClaims);
VerifyCustomToken(token, "user2", developerClaims);
@@ -71,37 +71,39 @@ public async Task InvalidUid()
await Assert.ThrowsAsync(
async () => await factory.CreateCustomTokenAsync(null));
await Assert.ThrowsAsync(
- async () => await factory.CreateCustomTokenAsync(""));
+ async () => await factory.CreateCustomTokenAsync(string.Empty));
await Assert.ThrowsAsync(
- async () => await factory.CreateCustomTokenAsync(new String('a', 129)));
+ async () => await factory.CreateCustomTokenAsync(new string('a', 129)));
}
[Fact]
public async Task ReservedClaims()
{
var factory = new FirebaseTokenFactory(new MockSigner(), new MockClock());
- foreach(var key in FirebaseTokenFactory.ReservedClaims)
+ foreach (var key in FirebaseTokenFactory.ReservedClaims)
{
- var developerClaims = new Dictionary(){
- {key, "value"},
+ var developerClaims = new Dictionary()
+ {
+ { key, "value" },
};
await Assert.ThrowsAsync(
- async () => await factory.CreateCustomTokenAsync("user", developerClaims));
- }
+ async () => await factory.CreateCustomTokenAsync("user", developerClaims));
+ }
}
private static void VerifyCustomToken(
string token, string uid, Dictionary claims)
{
- String[] segments = token.Split(".");
+ string[] segments = token.Split(".");
Assert.Equal(3, segments.Length);
+
// verify header
var header = JwtUtils.Decode(segments[0]);
Assert.Equal("JWT", header.Type);
Assert.Equal("RS256", header.Algorithm);
// verify payload
- var payload = JwtUtils.Decode(segments[1]);
+ var payload = JwtUtils.Decode(segments[1]);
Assert.Equal(MockSigner.KeyIdString, payload.Issuer);
Assert.Equal(MockSigner.KeyIdString, payload.Subject);
Assert.Equal(uid, payload.Uid);
@@ -141,6 +143,6 @@ public Task SignDataAsync(byte[] data, CancellationToken cancellationTok
return Task.FromResult(Encoding.UTF8.GetBytes(Signature));
}
- public void Dispose() {}
+ public void Dispose() { }
}
}
diff --git a/FirebaseAdmin/FirebaseAdmin.Tests/Auth/FirebaseTokenVerifierTest.cs b/FirebaseAdmin/FirebaseAdmin.Tests/Auth/FirebaseTokenVerifierTest.cs
index f8bfea99..306d3b5b 100644
--- a/FirebaseAdmin/FirebaseAdmin.Tests/Auth/FirebaseTokenVerifierTest.cs
+++ b/FirebaseAdmin/FirebaseAdmin.Tests/Auth/FirebaseTokenVerifierTest.cs
@@ -20,20 +20,23 @@
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
-using Xunit;
-using Google.Apis.Auth.OAuth2;
-using Google.Apis.Util;
using FirebaseAdmin.Auth;
using FirebaseAdmin.Tests;
+using Google.Apis.Auth.OAuth2;
+using Google.Apis.Util;
+using Xunit;
namespace FirebaseAdmin.Auth.Tests
{
- public class FirebaseTokenVerifierTest: IDisposable
+ public class FirebaseTokenVerifierTest : IDisposable
{
private static readonly IPublicKeySource KeySource = new FileSystemPublicKeySource(
"./resources/public_cert.pem");
+
private static readonly IClock Clock = new MockClock();
+
private static readonly ISigner Signer = CreateTestSigner();
+
private static readonly FirebaseTokenVerifier TokenVerifier = new FirebaseTokenVerifier(
new FirebaseTokenVerifierArgs()
{
@@ -46,7 +49,7 @@ public class FirebaseTokenVerifierTest: IDisposable
PublicKeySource = KeySource,
});
- private static readonly GoogleCredential mockCredential =
+ private static readonly GoogleCredential MockCredential =
GoogleCredential.FromAccessToken("test-token");
[Fact]
@@ -54,17 +57,18 @@ public async Task ValidToken()
{
var payload = new Dictionary()
{
- {"foo", "bar"},
+ { "foo", "bar" },
};
var idToken = await CreateTestTokenAsync(payloadOverrides: payload);
var decoded = await TokenVerifier.VerifyTokenAsync(idToken);
Assert.Equal("testuser", decoded.Uid);
Assert.Equal("test-project", decoded.Audience);
Assert.Equal("testuser", decoded.Subject);
+
// The default test token created by CreateTestTokenAsync has an issue time 10 minutes
// ago, and an expiry time 50 minutes in the future.
- Assert.Equal(Clock.UnixTimestamp() - 60 * 10, decoded.IssuedAtTimeSeconds);
- Assert.Equal(Clock.UnixTimestamp() + 60 * 50, decoded.ExpirationTimeSeconds);
+ Assert.Equal(Clock.UnixTimestamp() - (60 * 10), decoded.IssuedAtTimeSeconds);
+ Assert.Equal(Clock.UnixTimestamp() + (60 * 50), decoded.ExpirationTimeSeconds);
Assert.Single(decoded.Claims);
object value;
Assert.True(decoded.Claims.TryGetValue("foo", out value));
@@ -77,7 +81,7 @@ public async Task InvalidArgument()
await Assert.ThrowsAsync(
async () => await TokenVerifier.VerifyTokenAsync(null));
await Assert.ThrowsAsync(
- async () => await TokenVerifier.VerifyTokenAsync(""));
+ async () => await TokenVerifier.VerifyTokenAsync(string.Empty));
}
[Fact]
@@ -92,7 +96,7 @@ public async Task NoKid()
{
var header = new Dictionary()
{
- {"kid", ""},
+ { "kid", string.Empty },
};
var idToken = await CreateTestTokenAsync(headerOverrides: header);
await Assert.ThrowsAsync(
@@ -104,7 +108,7 @@ public async Task IncorrectKid()
{
var header = new Dictionary()
{
- {"kid", "incorrect-key-id"},
+ { "kid", "incorrect-key-id" },
};
var idToken = await CreateTestTokenAsync(headerOverrides: header);
await Assert.ThrowsAsync(
@@ -116,7 +120,7 @@ public async Task IncorrectAlgorithm()
{
var header = new Dictionary()
{
- {"alg", "HS256"},
+ { "alg", "HS256" },
};
var idToken = await CreateTestTokenAsync(headerOverrides: header);
await Assert.ThrowsAsync(
@@ -128,7 +132,7 @@ public async Task Expired()
{
var payload = new Dictionary()
{
- {"exp", Clock.UnixTimestamp() - 60},
+ { "exp", Clock.UnixTimestamp() - 60 },
};
var idToken = await CreateTestTokenAsync(payloadOverrides: payload);
await Assert.ThrowsAsync(
@@ -140,7 +144,7 @@ public async Task InvalidIssuedAt()
{
var payload = new Dictionary()
{
- {"iat", Clock.UnixTimestamp() + 60},
+ { "iat", Clock.UnixTimestamp() + 60 },
};
var idToken = await CreateTestTokenAsync(payloadOverrides: payload);
await Assert.ThrowsAsync(
@@ -152,7 +156,7 @@ public async Task InvalidIssuer()
{
var payload = new Dictionary()
{
- {"iss", "wrong-issuer"},
+ { "iss", "wrong-issuer" },
};
var idToken = await CreateTestTokenAsync(payloadOverrides: payload);
await Assert.ThrowsAsync(
@@ -164,7 +168,7 @@ public async Task InvalidAudience()
{
var payload = new Dictionary()
{
- {"aud", "wrong-audience"},
+ { "aud", "wrong-audience" },
};
var idToken = await CreateTestTokenAsync(payloadOverrides: payload);
await Assert.ThrowsAsync(
@@ -176,7 +180,7 @@ public async Task EmptySubject()
{
var payload = new Dictionary()
{
- {"sub", ""},
+ { "sub", string.Empty },
};
var idToken = await CreateTestTokenAsync(payloadOverrides: payload);
await Assert.ThrowsAsync(
@@ -188,7 +192,7 @@ public async Task LongSubject()
{
var payload = new Dictionary()
{
- {"sub", new String('a', 129)},
+ { "sub", new string('a', 129) },
};
var idToken = await CreateTestTokenAsync(payloadOverrides: payload);
await Assert.ThrowsAsync(
@@ -200,7 +204,7 @@ public void ProjectIdFromOptions()
{
var app = FirebaseApp.Create(new AppOptions()
{
- Credential = mockCredential,
+ Credential = MockCredential,
ProjectId = "explicit-project-id",
});
var verifier = FirebaseTokenVerifier.CreateIDTokenVerifier(app);
@@ -226,14 +230,14 @@ public void ProjectIdFromEnvironment()
{
var app = FirebaseApp.Create(new AppOptions()
{
- Credential = mockCredential,
+ Credential = MockCredential,
});
var verifier = FirebaseTokenVerifier.CreateIDTokenVerifier(app);
Assert.Equal("env-project-id", verifier.ProjectId);
}
finally
{
- Environment.SetEnvironmentVariable("GOOGLE_CLOUD_PROJECT", "");
+ Environment.SetEnvironmentVariable("GOOGLE_CLOUD_PROJECT", string.Empty);
}
}
@@ -253,9 +257,9 @@ internal static async Task CreateTestTokenAsync(
{
var header = new Dictionary()
{
- {"alg", "RS256"},
- {"typ", "jwt"},
- {"kid", "test-key-id"},
+ { "alg", "RS256" },
+ { "typ", "jwt" },
+ { "kid", "test-key-id" },
};
if (headerOverrides != null)
{
@@ -267,11 +271,11 @@ internal static async Task CreateTestTokenAsync(
var payload = new Dictionary()
{
- {"sub", "testuser"},
- {"iss", "https://securetoken.google.com/test-project"},
- {"aud", "test-project"},
- {"iat", Clock.UnixTimestamp() - 60 * 10},
- {"exp", Clock.UnixTimestamp() + 60 * 50},
+ { "sub", "testuser" },
+ { "iss", "https://securetoken.google.com/test-project" },
+ { "aud", "test-project" },
+ { "iat", Clock.UnixTimestamp() - (60 * 10) },
+ { "exp", Clock.UnixTimestamp() + (60 * 50) },
};
if (payloadOverrides != null)
{
@@ -280,32 +284,33 @@ internal static async Task CreateTestTokenAsync(
payload[entry.Key] = entry.Value;
}
}
+
return await JwtUtils.CreateSignedJwtAsync(header, payload, Signer);
}
private static ISigner CreateTestSigner()
{
var credential = GoogleCredential.FromFile("./resources/service_account.json");
- var serviceAccount = (ServiceAccountCredential) credential.UnderlyingCredential;
+ var serviceAccount = (ServiceAccountCredential)credential.UnderlyingCredential;
return new ServiceAccountSigner(serviceAccount);
}
}
internal class FileSystemPublicKeySource : IPublicKeySource
{
- private IReadOnlyList _rsa;
+ private IReadOnlyList rsa;
public FileSystemPublicKeySource(string file)
{
var x509cert = new X509Certificate2(File.ReadAllBytes(file));
- var rsa = (RSA) x509cert.PublicKey.Key;
- _rsa = ImmutableList.Create(new PublicKey("test-key-id", rsa));
+ var rsa = (RSA)x509cert.PublicKey.Key;
+ this.rsa = ImmutableList.Create(new PublicKey("test-key-id", rsa));
}
public Task> GetPublicKeysAsync(
CancellationToken cancellationToken)
{
- return Task.FromResult(_rsa);
+ return Task.FromResult(this.rsa);
}
}
}
diff --git a/FirebaseAdmin/FirebaseAdmin.Tests/Auth/FirebaseUserManagerTest.cs b/FirebaseAdmin/FirebaseAdmin.Tests/Auth/FirebaseUserManagerTest.cs
index 3b666c49..c3114fe2 100644
--- a/FirebaseAdmin/FirebaseAdmin.Tests/Auth/FirebaseUserManagerTest.cs
+++ b/FirebaseAdmin/FirebaseAdmin.Tests/Auth/FirebaseUserManagerTest.cs
@@ -14,25 +14,26 @@
using System;
using System.Collections.Generic;
-using Xunit;
-using Google.Apis.Auth.OAuth2;
-using FirebaseAdmin.Tests;
-using System.Threading.Tasks;
using System.Net;
+using System.Threading.Tasks;
+using FirebaseAdmin.Tests;
+using Google.Apis.Auth.OAuth2;
+using Xunit;
namespace FirebaseAdmin.Auth.Tests
{
public class FirebaseUserManagerTest
{
- private static readonly GoogleCredential mockCredential =
+ private const string MockProjectId = "project1";
+
+ private static readonly GoogleCredential MockCredential =
GoogleCredential.FromAccessToken("test-token");
- private const string mockProjectId = "project1";
[Fact]
public void InvalidUidForUserRecord()
{
Assert.Throws(() => new UserRecord(null));
- Assert.Throws(() => new UserRecord(""));
+ Assert.Throws(() => new UserRecord(string.Empty));
Assert.Throws(() => new UserRecord(new string('a', 129)));
}
@@ -41,18 +42,20 @@ public void ReservedClaims()
{
foreach (var key in FirebaseTokenFactory.ReservedClaims)
{
- var customClaims = new Dictionary(){
- {key, "value"},
+ var customClaims = new Dictionary()
+ {
+ { key, "value" },
};
- Assert.Throws(() => new UserRecord("user1") { CustomClaims = customClaims});
+ Assert.Throws(() => new UserRecord("user1") { CustomClaims = customClaims });
}
}
[Fact]
public void EmptyClaims()
{
- var emptyClaims = new Dictionary(){
- {"", "value"},
+ var emptyClaims = new Dictionary()
+ {
+ { string.Empty, "value" },
};
Assert.Throws(() => new UserRecord("user1") { CustomClaims = emptyClaims });
}
@@ -73,18 +76,19 @@ public async Task UpdateUser()
{
var handler = new MockMessageHandler()
{
- Response = new UserRecord("user1")
+ Response = new UserRecord("user1"),
};
var factory = new MockHttpClientFactory(handler);
var userManager = new FirebaseUserManager(
new FirebaseUserManagerArgs
{
- Credential = mockCredential,
- ProjectId = mockProjectId,
- ClientFactory = factory
+ Credential = MockCredential,
+ ProjectId = MockProjectId,
+ ClientFactory = factory,
});
- var customClaims = new Dictionary(){
- {"admin", true},
+ var customClaims = new Dictionary()
+ {
+ { "admin", true },
};
await userManager.UpdateUserAsync(new UserRecord("user1") { CustomClaims = customClaims });
@@ -95,18 +99,19 @@ public async Task UpdateUserIncorrectResponseObject()
{
var handler = new MockMessageHandler()
{
- Response = new object()
+ Response = new object(),
};
var factory = new MockHttpClientFactory(handler);
var userManager = new FirebaseUserManager(
new FirebaseUserManagerArgs
{
- Credential = mockCredential,
- ProjectId = mockProjectId,
- ClientFactory = factory
+ Credential = MockCredential,
+ ProjectId = MockProjectId,
+ ClientFactory = factory,
});
- var customClaims = new Dictionary(){
- {"admin", true},
+ var customClaims = new Dictionary()
+ {
+ { "admin", true },
};
await Assert.ThrowsAsync(
@@ -118,18 +123,19 @@ public async Task UpdateUserIncorrectResponseUid()
{
var handler = new MockMessageHandler()
{
- Response = new UserRecord("testuser")
+ Response = new UserRecord("testuser"),
};
var factory = new MockHttpClientFactory(handler);
var userManager = new FirebaseUserManager(
new FirebaseUserManagerArgs
{
- Credential = mockCredential,
- ProjectId = mockProjectId,
- ClientFactory = factory
+ Credential = MockCredential,
+ ProjectId = MockProjectId,
+ ClientFactory = factory,
});
- var customClaims = new Dictionary(){
- {"admin", true},
+ var customClaims = new Dictionary()
+ {
+ { "admin", true },
};
await Assert.ThrowsAsync(
@@ -141,18 +147,19 @@ public async Task UpdateUserHttpError()
{
var handler = new MockMessageHandler()
{
- StatusCode = HttpStatusCode.InternalServerError
+ StatusCode = HttpStatusCode.InternalServerError,
};
var factory = new MockHttpClientFactory(handler);
var userManager = new FirebaseUserManager(
new FirebaseUserManagerArgs
{
- Credential = mockCredential,
- ProjectId = mockProjectId,
- ClientFactory = factory
+ Credential = MockCredential,
+ ProjectId = MockProjectId,
+ ClientFactory = factory,
});
- var customClaims = new Dictionary(){
- {"admin", true},
+ var customClaims = new Dictionary()
+ {
+ { "admin", true },
};
await Assert.ThrowsAsync(
diff --git a/FirebaseAdmin/FirebaseAdmin.Tests/Auth/HttpPublicKeySourceTest.cs b/FirebaseAdmin/FirebaseAdmin.Tests/Auth/HttpPublicKeySourceTest.cs
index e7563010..cf4406b4 100644
--- a/FirebaseAdmin/FirebaseAdmin.Tests/Auth/HttpPublicKeySourceTest.cs
+++ b/FirebaseAdmin/FirebaseAdmin.Tests/Auth/HttpPublicKeySourceTest.cs
@@ -18,9 +18,9 @@
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
-using Xunit;
-using FirebaseAdmin.Tests;
using FirebaseAdmin.Auth;
+using FirebaseAdmin.Tests;
+using Xunit;
namespace FirebaseAdmin.Auth.Tests
{
@@ -92,7 +92,7 @@ public void InvalidArguments()
Assert.Throws(
() => new HttpPublicKeySource(null, clock, clientFactory));
Assert.Throws(
- () => new HttpPublicKeySource("", clock, clientFactory));
+ () => new HttpPublicKeySource(string.Empty, clock, clientFactory));
Assert.Throws(
() => new HttpPublicKeySource("https://example.com/certs", null, clientFactory));
Assert.Throws(
diff --git a/FirebaseAdmin/FirebaseAdmin.Tests/Auth/IAMSignerTest.cs b/FirebaseAdmin/FirebaseAdmin.Tests/Auth/IAMSignerTest.cs
index 20815944..3bd82579 100644
--- a/FirebaseAdmin/FirebaseAdmin.Tests/Auth/IAMSignerTest.cs
+++ b/FirebaseAdmin/FirebaseAdmin.Tests/Auth/IAMSignerTest.cs
@@ -19,14 +19,65 @@
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using FirebaseAdmin.Tests;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Http;
using Google.Apis.Json;
using Xunit;
-using FirebaseAdmin.Tests;
namespace FirebaseAdmin.Auth.Tests
{
+ public class IAMSignerTest
+ {
+ [Fact]
+ public async Task Signer()
+ {
+ var bytes = Encoding.UTF8.GetBytes("signature");
+ var handler = new MockMessageHandler()
+ {
+ Response = "discovered-service-account",
+ };
+ var factory = new MockHttpClientFactory(handler);
+ var signer = new IAMSigner(factory, GoogleCredential.FromAccessToken("token"));
+ Assert.Equal("discovered-service-account", await signer.GetKeyIdAsync());
+ Assert.Equal(1, handler.Calls);
+
+ // should only fetch account once
+ Assert.Equal("discovered-service-account", await signer.GetKeyIdAsync());
+ Assert.Equal(1, handler.Calls);
+
+ handler.Response = new IAMSigner.SignBlobResponse()
+ {
+ Signature = Convert.ToBase64String(bytes),
+ };
+ byte[] data = Encoding.UTF8.GetBytes("Hello world");
+ byte[] signature = await signer.SignDataAsync(data);
+ Assert.Equal(bytes, signature);
+ var req = NewtonsoftJsonSerializer.Instance.Deserialize(
+ handler.Request);
+ Assert.Equal(Convert.ToBase64String(data), req.BytesToSign);
+ Assert.Equal(2, handler.Calls);
+ }
+
+ [Fact]
+ public async Task AccountDiscoveryError()
+ {
+ var bytes = Encoding.UTF8.GetBytes("signature");
+ var handler = new MockMessageHandler()
+ {
+ StatusCode = HttpStatusCode.InternalServerError,
+ };
+ var factory = new MockHttpClientFactory(handler);
+ var signer = new IAMSigner(factory, GoogleCredential.FromAccessToken("token"));
+ await Assert.ThrowsAsync(
+ async () => await signer.GetKeyIdAsync());
+ Assert.Equal(1, handler.Calls);
+ await Assert.ThrowsAsync(
+ async () => await signer.GetKeyIdAsync());
+ Assert.Equal(1, handler.Calls);
+ }
+ }
+
public class FixedAccountIAMSignerTest
{
[Fact]
@@ -35,7 +86,7 @@ public async Task Signer()
var bytes = Encoding.UTF8.GetBytes("signature");
var handler = new MockMessageHandler()
{
- Response = new SignBlobResponse()
+ Response = new IAMSigner.SignBlobResponse()
{
Signature = Convert.ToBase64String(bytes),
},
@@ -47,7 +98,7 @@ public async Task Signer()
byte[] data = Encoding.UTF8.GetBytes("Hello world");
byte[] signature = await signer.SignDataAsync(data);
Assert.Equal(bytes, signature);
- var req = NewtonsoftJsonSerializer.Instance.Deserialize(
+ var req = NewtonsoftJsonSerializer.Instance.Deserialize(
handler.Request);
Assert.Equal(Convert.ToBase64String(data), req.BytesToSign);
Assert.Equal(1, handler.Calls);
@@ -59,7 +110,7 @@ public async Task WelformedSignError()
var handler = new MockMessageHandler()
{
StatusCode = HttpStatusCode.InternalServerError,
- Response = @"{""error"": {""message"": ""test reason""}}"
+ Response = @"{""error"": {""message"": ""test reason""}}",
};
var factory = new MockHttpClientFactory(handler);
var signer = new FixedAccountIAMSigner(
@@ -77,7 +128,7 @@ public async Task UnexpectedSignError()
var handler = new MockMessageHandler()
{
StatusCode = HttpStatusCode.InternalServerError,
- Response = "not json"
+ Response = "not json",
};
var factory = new MockHttpClientFactory(handler);
var signer = new FixedAccountIAMSigner(
@@ -89,55 +140,4 @@ public async Task UnexpectedSignError()
Assert.Contains("not json", ex.Message);
}
}
-
- public class IAMSignerTest
- {
- [Fact]
- public async Task Signer()
- {
- var bytes = Encoding.UTF8.GetBytes("signature");
- var handler = new MockMessageHandler()
- {
- Response = "discovered-service-account",
- };
- var factory = new MockHttpClientFactory(handler);
- var signer = new IAMSigner(factory, GoogleCredential.FromAccessToken("token"));
- Assert.Equal("discovered-service-account", await signer.GetKeyIdAsync());
- Assert.Equal(1, handler.Calls);
-
- // should only fetch account once
- Assert.Equal("discovered-service-account", await signer.GetKeyIdAsync());
- Assert.Equal(1, handler.Calls);
-
- handler.Response = new SignBlobResponse()
- {
- Signature = Convert.ToBase64String(bytes),
- };
- byte[] data = Encoding.UTF8.GetBytes("Hello world");
- byte[] signature = await signer.SignDataAsync(data);
- Assert.Equal(bytes, signature);
- var req = NewtonsoftJsonSerializer.Instance.Deserialize(
- handler.Request);
- Assert.Equal(Convert.ToBase64String(data), req.BytesToSign);
- Assert.Equal(2, handler.Calls);
- }
-
- [Fact]
- public async Task AccountDiscoveryError()
- {
- var bytes = Encoding.UTF8.GetBytes("signature");
- var handler = new MockMessageHandler()
- {
- StatusCode = HttpStatusCode.InternalServerError,
- };
- var factory = new MockHttpClientFactory(handler);
- var signer = new IAMSigner(factory, GoogleCredential.FromAccessToken("token"));
- await Assert.ThrowsAsync(
- async () => await signer.GetKeyIdAsync());
- Assert.Equal(1, handler.Calls);
- await Assert.ThrowsAsync(
- async () => await signer.GetKeyIdAsync());
- Assert.Equal(1, handler.Calls);
- }
- }
}
diff --git a/FirebaseAdmin/FirebaseAdmin.Tests/Auth/ServiceAccountSignerTest.cs b/FirebaseAdmin/FirebaseAdmin.Tests/Auth/ServiceAccountSignerTest.cs
index 2874a79f..cf76f125 100644
--- a/FirebaseAdmin/FirebaseAdmin.Tests/Auth/ServiceAccountSignerTest.cs
+++ b/FirebaseAdmin/FirebaseAdmin.Tests/Auth/ServiceAccountSignerTest.cs
@@ -18,9 +18,9 @@
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
-using Xunit;
using FirebaseAdmin.Auth;
using Google.Apis.Auth.OAuth2;
+using Xunit;
namespace FirebaseAdmin.Auth.Tests
{
@@ -30,13 +30,13 @@ public class ServiceAccountSignerTest
public async Task Signer()
{
var credential = GoogleCredential.FromFile("./resources/service_account.json");
- var serviceAccount = (ServiceAccountCredential) credential.UnderlyingCredential;
+ var serviceAccount = (ServiceAccountCredential)credential.UnderlyingCredential;
var signer = new ServiceAccountSigner(serviceAccount);
- Assert.Equal("client@test-project.iam.gserviceaccount.com",
- await signer.GetKeyIdAsync());
+ Assert.Equal(
+ "client@test-project.iam.gserviceaccount.com", await signer.GetKeyIdAsync());
byte[] data = Encoding.UTF8.GetBytes("Hello world");
byte[] signature = signer.SignDataAsync(data).Result;
- Assert.True(Verify(data, signature));
+ Assert.True(this.Verify(data, signature));
}
[Fact]
@@ -48,7 +48,7 @@ public void NullCredential()
private bool Verify(byte[] data, byte[] signature)
{
var x509cert = new X509Certificate2(File.ReadAllBytes("./resources/public_cert.pem"));
- var rsa = (RSA) x509cert.PublicKey.Key;
+ var rsa = (RSA)x509cert.PublicKey.Key;
return rsa.VerifyData(
data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
diff --git a/FirebaseAdmin/FirebaseAdmin.Tests/FirebaseAdmin.Tests.csproj b/FirebaseAdmin/FirebaseAdmin.Tests/FirebaseAdmin.Tests.csproj
index fd4c9036..8641fa7b 100644
--- a/FirebaseAdmin/FirebaseAdmin.Tests/FirebaseAdmin.Tests.csproj
+++ b/FirebaseAdmin/FirebaseAdmin.Tests/FirebaseAdmin.Tests.csproj
@@ -5,6 +5,8 @@
false
../../FirebaseAdmin.snk
true
+ true
+ ../../stylecop_test.ruleset
@@ -13,6 +15,9 @@
+
+ all
+
diff --git a/FirebaseAdmin/FirebaseAdmin.Tests/FirebaseAppTest.cs b/FirebaseAdmin/FirebaseAdmin.Tests/FirebaseAppTest.cs
index cc525acd..5a453821 100644
--- a/FirebaseAdmin/FirebaseAdmin.Tests/FirebaseAppTest.cs
+++ b/FirebaseAdmin/FirebaseAdmin.Tests/FirebaseAppTest.cs
@@ -14,13 +14,13 @@
using System;
using System.Threading.Tasks;
-using Xunit;
using FirebaseAdmin;
using Google.Apis.Auth.OAuth2;
+using Xunit;
namespace FirebaseAdmin.Tests
{
- public class FirebaseAppTest: IDisposable
+ public class FirebaseAppTest : IDisposable
{
private static readonly AppOptions TestOptions = new AppOptions()
{
@@ -96,7 +96,7 @@ public void CreateAppOptions()
};
var app = FirebaseApp.Create(options);
Assert.Equal("[DEFAULT]", app.Name);
-
+
var copy = app.Options;
Assert.NotSame(options, copy);
Assert.Same(credential, copy.Credential);
@@ -114,7 +114,7 @@ public void ServiceAccountCredentialScoping()
};
var app = FirebaseApp.Create(options);
Assert.Equal("[DEFAULT]", app.Name);
-
+
var copy = app.Options;
Assert.NotSame(options, copy);
Assert.NotSame(credential, copy.Credential);
@@ -139,7 +139,7 @@ public void ApplicationDefaultCredentials()
}
finally
{
- Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", "");
+ Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", string.Empty);
}
}
@@ -169,7 +169,7 @@ public void GetProjectIdFromServiceAccount()
[Fact]
public void GetProjectIdFromEnvironment()
{
- foreach (var name in new string[]{"GOOGLE_CLOUD_PROJECT", "GCLOUD_PROJECT"})
+ foreach (var name in new string[] { "GOOGLE_CLOUD_PROJECT", "GCLOUD_PROJECT" })
{
Environment.SetEnvironmentVariable(name, "env-project");
try
@@ -180,7 +180,7 @@ public void GetProjectIdFromEnvironment()
}
finally
{
- Environment.SetEnvironmentVariable(name, "");
+ Environment.SetEnvironmentVariable(name, string.Empty);
}
}
}
@@ -196,14 +196,15 @@ public void GetOrInitService()
var service1 = app.GetOrInit("MockService", factory);
var service2 = app.GetOrInit("MockService", factory);
Assert.Same(service1, service2);
- Assert.Throws(() => {
+ Assert.Throws(() =>
+ {
app.GetOrInit("MockService", () => { return new OtherMockService(); });
});
-
+
Assert.False(service1.Deleted);
app.Delete();
Assert.True(service1.Deleted);
- Assert.Throws(() =>
+ Assert.Throws(() =>
{
app.GetOrInit("MockService", factory);
});
@@ -211,22 +212,22 @@ public void GetOrInitService()
public void Dispose()
{
- FirebaseApp.DeleteAll();
+ FirebaseApp.DeleteAll();
}
}
- internal class MockService: IFirebaseService
+ internal class MockService : IFirebaseService
{
public bool Deleted { get; private set; }
public void Delete()
{
- Deleted = true;
+ this.Deleted = true;
}
}
- internal class OtherMockService: IFirebaseService
+ internal class OtherMockService : IFirebaseService
{
- public void Delete() {}
+ public void Delete() { }
}
}
diff --git a/FirebaseAdmin/FirebaseAdmin.Tests/MockClock.cs b/FirebaseAdmin/FirebaseAdmin.Tests/MockClock.cs
index d4fb09e6..f68f7785 100644
--- a/FirebaseAdmin/FirebaseAdmin.Tests/MockClock.cs
+++ b/FirebaseAdmin/FirebaseAdmin.Tests/MockClock.cs
@@ -19,36 +19,37 @@ namespace FirebaseAdmin.Tests
{
public class MockClock : IClock
{
- public DateTime Now
+ private object mutex = new object();
+ private DateTime utcNow;
+
+ public MockClock()
{
- get { return UtcNow.ToLocalTime(); }
- set { UtcNow = value.ToUniversalTime(); }
+ this.Now = DateTime.Now;
}
- private object _lock = new object();
- private DateTime _utcNow;
+ public DateTime Now
+ {
+ get { return this.UtcNow.ToLocalTime(); }
+ set { this.UtcNow = value.ToUniversalTime(); }
+ }
public DateTime UtcNow
{
get
{
- lock (_lock)
+ lock (this.mutex)
{
- return _utcNow;
+ return this.utcNow;
}
}
+
set
{
- lock (_lock)
+ lock (this.mutex)
{
- _utcNow = value;
+ this.utcNow = value;
}
}
}
-
- public MockClock()
- {
- Now = DateTime.Now;
- }
}
}
diff --git a/FirebaseAdmin/FirebaseAdmin.Tests/MockMessageHandler.cs b/FirebaseAdmin/FirebaseAdmin.Tests/MockMessageHandler.cs
index 7bc4e209..b6456e79 100644
--- a/FirebaseAdmin/FirebaseAdmin.Tests/MockMessageHandler.cs
+++ b/FirebaseAdmin/FirebaseAdmin.Tests/MockMessageHandler.cs
@@ -26,99 +26,103 @@
namespace FirebaseAdmin.Tests
{
- internal class MockHttpClientFactory : HttpClientFactory
- {
- private HttpMessageHandler Handler { get; set; }
-
- public MockHttpClientFactory(HttpMessageHandler handler)
- {
- Handler = handler;
- }
-
- protected override HttpMessageHandler CreateHandler(CreateHttpClientArgs args)
- {
- return Handler;
- }
- }
-
///
/// An implementation that counts the number of requests
- /// processed.
+ /// and facilitates mocking HTTP interactions locally.
///
- internal abstract class CountableMessageHandler : HttpMessageHandler
+ internal class MockMessageHandler : CountableMessageHandler
{
- private int _calls;
-
- public int Calls
- {
- get { return _calls; }
- }
-
- sealed protected override Task SendAsync(
- HttpRequestMessage request, CancellationToken cancellationToken)
+ public MockMessageHandler()
{
- Interlocked.Increment(ref _calls);
- return SendAsyncCore(request, cancellationToken);
+ this.StatusCode = HttpStatusCode.OK;
}
- protected abstract Task SendAsyncCore(
- HttpRequestMessage request, CancellationToken cancellationToken);
- }
+ public delegate void SetHeaders(HttpResponseHeaders header);
- ///
- /// An implementation that counts the number of requests
- /// and facilitates mocking HTTP interactions locally.
- ///
- internal class MockMessageHandler : CountableMessageHandler
- {
public string Request { get; private set; }
-
+
public HttpStatusCode StatusCode { get; set; }
- public Object Response { get; set; }
- public delegate void SetHeaders(HttpResponseHeaders header);
+ public object Response { get; set; }
public SetHeaders ApplyHeaders { get; set; }
- public MockMessageHandler()
- {
- StatusCode = HttpStatusCode.OK;
- }
-
protected override async Task SendAsyncCore(
HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.Content != null)
{
- Request = await request.Content.ReadAsStringAsync();
+ this.Request = await request.Content.ReadAsStringAsync();
}
else
{
- Request = null;
- }
+ this.Request = null;
+ }
+
var resp = new HttpResponseMessage();
string json;
- if (Response is byte[])
+ if (this.Response is byte[])
{
- json = Encoding.UTF8.GetString(Response as byte[]);
+ json = Encoding.UTF8.GetString(this.Response as byte[]);
}
- else if (Response is string)
+ else if (this.Response is string)
{
- json = Response as string;
+ json = this.Response as string;
}
else
{
- json = NewtonsoftJsonSerializer.Instance.Serialize(Response);
- }
- resp.StatusCode = StatusCode;
- if (ApplyHeaders != null)
+ json = NewtonsoftJsonSerializer.Instance.Serialize(this.Response);
+ }
+
+ resp.StatusCode = this.StatusCode;
+ if (this.ApplyHeaders != null)
{
- ApplyHeaders(resp.Headers);
+ this.ApplyHeaders(resp.Headers);
}
+
resp.Content = new StringContent(json, Encoding.UTF8, "application/json");
var tcs = new TaskCompletionSource();
tcs.SetResult(resp);
return await tcs.Task;
}
}
+
+ ///
+ /// An implementation that counts the number of requests
+ /// processed.
+ ///
+ internal abstract class CountableMessageHandler : HttpMessageHandler
+ {
+ private int calls;
+
+ public int Calls
+ {
+ get { return this.calls; }
+ }
+
+ protected sealed override Task SendAsync(
+ HttpRequestMessage request, CancellationToken cancellationToken)
+ {
+ Interlocked.Increment(ref this.calls);
+ return this.SendAsyncCore(request, cancellationToken);
+ }
+
+ protected abstract Task SendAsyncCore(
+ HttpRequestMessage request, CancellationToken cancellationToken);
+ }
+
+ internal class MockHttpClientFactory : HttpClientFactory
+ {
+ private readonly HttpMessageHandler handler;
+
+ public MockHttpClientFactory(HttpMessageHandler handler)
+ {
+ this.handler = handler;
+ }
+
+ protected override HttpMessageHandler CreateHandler(CreateHttpClientArgs args)
+ {
+ return this.handler;
+ }
+ }
}
diff --git a/FirebaseAdmin/FirebaseAdmin/AppOptions.cs b/FirebaseAdmin/FirebaseAdmin/AppOptions.cs
index e5ff1fbc..9d529ac5 100644
--- a/FirebaseAdmin/FirebaseAdmin/AppOptions.cs
+++ b/FirebaseAdmin/FirebaseAdmin/AppOptions.cs
@@ -26,35 +26,37 @@ namespace FirebaseAdmin
public sealed class AppOptions
{
///
- /// used to authorize an app. All service calls made by
- /// the app will be authorized using this.
+ /// Initializes a new instance of the class.
+ ///
+ public AppOptions() { }
+
+ internal AppOptions(AppOptions options)
+ {
+ this.Credential = options.Credential;
+ this.ProjectId = options.ProjectId;
+ this.ServiceAccountId = options.ServiceAccountId;
+ }
+
+ ///
+ /// Gets or sets the used to authorize an app. All service
+ /// calls made by the app will be authorized using this.
///
public GoogleCredential Credential { get; set; }
///
- /// The Google Cloud Platform project ID that should be associated with an app.
+ /// Gets or sets the Google Cloud Platform project ID that should be associated with an
+ /// app.
///
public string ProjectId { get; set; }
///
- /// The unique ID of the service account that should be associated with an app.
+ /// Gets or sets the unique ID of the service account that should be associated with an
+ /// app.
/// This is used to
/// create custom auth tokens when service account credentials are not available. The
/// service account ID can be found in the client_email field of the service account
/// JSON.
///
public string ServiceAccountId { get; set; }
-
- ///
- /// Creates a new instance.
- ///
- public AppOptions() {}
-
- internal AppOptions(AppOptions options)
- {
- Credential = options.Credential;
- ProjectId = options.ProjectId;
- ServiceAccountId = options.ServiceAccountId;
- }
}
}
diff --git a/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseAuth.cs b/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseAuth.cs
index f01e2b7d..a07419db 100644
--- a/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseAuth.cs
+++ b/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseAuth.cs
@@ -23,24 +23,62 @@ namespace FirebaseAdmin.Auth
/// This is the entry point to all server-side Firebase Authentication operations. You can
/// get an instance of this class via FirebaseAuth.DefaultInstance.
///
- public sealed class FirebaseAuth: IFirebaseService
+ public sealed class FirebaseAuth : IFirebaseService
{
- private readonly FirebaseApp _app;
- private bool _deleted;
- private readonly Lazy _tokenFactory;
- private readonly Lazy _idTokenVerifier;
- private readonly Lazy _userManager;
- private readonly Object _lock = new Object();
+ private readonly FirebaseApp app;
+ private readonly Lazy tokenFactory;
+ private readonly Lazy idTokenVerifier;
+ private readonly Lazy userManager;
+ private readonly object authLock = new object();
+ private bool deleted;
private FirebaseAuth(FirebaseApp app)
{
- _app = app;
- _tokenFactory = new Lazy(() =>
- FirebaseTokenFactory.Create(_app), true);
- _idTokenVerifier = new Lazy(() =>
- FirebaseTokenVerifier.CreateIDTokenVerifier(_app), true);
- _userManager = new Lazy(() =>
- FirebaseUserManager.Create(_app));
+ this.app = app;
+ this.tokenFactory = new Lazy(
+ () => FirebaseTokenFactory.Create(this.app), true);
+ this.idTokenVerifier = new Lazy(
+ () => FirebaseTokenVerifier.CreateIDTokenVerifier(this.app), true);
+ this.userManager = new Lazy(() =>
+ FirebaseUserManager.Create(this.app));
+ }
+
+ ///
+ /// Gets the auth instance associated with the default Firebase app. This property is
+ /// null if the default app doesn't yet exist.
+ ///
+ public static FirebaseAuth DefaultInstance
+ {
+ get
+ {
+ var app = FirebaseApp.DefaultInstance;
+ if (app == null)
+ {
+ return null;
+ }
+
+ return GetAuth(app);
+ }
+ }
+
+ ///
+ /// Returns the auth instance for the specified app.
+ ///
+ /// The instance associated with the specified
+ /// app.
+ /// If the app argument is null.
+ /// An app instance.
+ public static FirebaseAuth GetAuth(FirebaseApp app)
+ {
+ if (app == null)
+ {
+ throw new ArgumentNullException("App argument must not be null.");
+ }
+
+ return app.GetOrInit(typeof(FirebaseAuth).Name, () =>
+ {
+ return new FirebaseAuth(app);
+ });
}
///
@@ -76,7 +114,7 @@ private FirebaseAuth(FirebaseApp app)
/// 128 characters.
public async Task CreateCustomTokenAsync(string uid)
{
- return await CreateCustomTokenAsync(uid, default(CancellationToken));
+ return await this.CreateCustomTokenAsync(uid, default(CancellationToken));
}
///
@@ -115,7 +153,7 @@ public async Task CreateCustomTokenAsync(string uid)
public async Task CreateCustomTokenAsync(
string uid, CancellationToken cancellationToken)
{
- return await CreateCustomTokenAsync(uid, null, cancellationToken);
+ return await this.CreateCustomTokenAsync(uid, null, cancellationToken);
}
///
@@ -142,7 +180,7 @@ public async Task CreateCustomTokenAsync(
public async Task CreateCustomTokenAsync(
string uid, IDictionary developerClaims)
{
- return await CreateCustomTokenAsync(uid, developerClaims, default(CancellationToken));
+ return await this.CreateCustomTokenAsync(uid, developerClaims, default(CancellationToken));
}
///
@@ -174,14 +212,16 @@ public async Task CreateCustomTokenAsync(
CancellationToken cancellationToken)
{
FirebaseTokenFactory tokenFactory;
- lock (_lock)
+ lock (this.authLock)
{
- if (_deleted)
+ if (this.deleted)
{
throw new InvalidOperationException("Cannot invoke after deleting the app.");
}
- tokenFactory = _tokenFactory.Value;
+
+ tokenFactory = this.tokenFactory.Value;
}
+
return await tokenFactory.CreateCustomTokenAsync(
uid, developerClaims, cancellationToken).ConfigureAwait(false);
}
@@ -204,7 +244,7 @@ public async Task CreateCustomTokenAsync(
/// A Firebase ID token string to parse and verify.
public async Task VerifyIdTokenAsync(string idToken)
{
- return await VerifyIdTokenAsync(idToken, default(CancellationToken));
+ return await this.VerifyIdTokenAsync(idToken, default(CancellationToken));
}
///
@@ -228,14 +268,15 @@ public async Task VerifyIdTokenAsync(string idToken)
public async Task VerifyIdTokenAsync(
string idToken, CancellationToken cancellationToken)
{
- lock (_lock)
+ lock (this.authLock)
{
- if (_deleted)
+ if (this.deleted)
{
throw new InvalidOperationException("Cannot invoke after deleting the app.");
}
}
- return await _idTokenVerifier.Value.VerifyTokenAsync(idToken, cancellationToken)
+
+ return await this.idTokenVerifier.Value.VerifyTokenAsync(idToken, cancellationToken)
.ConfigureAwait(false);
}
@@ -244,6 +285,7 @@ public async Task VerifyIdTokenAsync(
/// removes any claims currently set on the user account. The claims must serialize into
/// a valid JSON string. The serialized claims must not be larger than 1000 characters.
///
+ /// A task that completes when the claims have been set.
/// If is null, empty or longer
/// than 128 characters. Or, if the serialized is larger than 1000
/// characters.
@@ -255,9 +297,9 @@ public async Task VerifyIdTokenAsync(
/// serialized claims should not be larger than 1000 characters.
public async Task SetCustomUserClaimsAsync(string uid, IReadOnlyDictionary claims)
{
- lock (_lock)
+ lock (this.authLock)
{
- if (_deleted)
+ if (this.deleted)
{
throw new InvalidOperationException("Cannot invoke after deleting the app.");
}
@@ -265,58 +307,30 @@ public async Task SetCustomUserClaimsAsync(string uid, IReadOnlyDictionary
+ /// Deletes this service instance.
+ ///
void IFirebaseService.Delete()
{
- lock (_lock)
+ lock (this.authLock)
{
- _deleted = true;
- if (_tokenFactory.IsValueCreated)
+ this.deleted = true;
+ if (this.tokenFactory.IsValueCreated)
{
- _tokenFactory.Value.Dispose();
+ this.tokenFactory.Value.Dispose();
}
- }
- }
- ///
- /// The auth instance associated with the default Firebase app. This property is
- /// null if the default app doesn't yet exist.
- ///
- public static FirebaseAuth DefaultInstance
- {
- get
- {
- var app = FirebaseApp.DefaultInstance;
- if (app == null)
+ if (this.userManager.IsValueCreated)
{
- return null;
+ this.userManager.Value.Dispose();
}
- return GetAuth(app);
}
}
-
- ///
- /// Returns the auth instance for the specified app.
- ///
- /// The instance associated with the specified
- /// app.
- /// If the app argument is null.
- /// An app instance.
- public static FirebaseAuth GetAuth(FirebaseApp app)
- {
- if (app == null)
- {
- throw new ArgumentNullException("App argument must not be null.");
- }
- return app.GetOrInit(typeof(FirebaseAuth).Name, () =>
- {
- return new FirebaseAuth(app);
- });
- }
}
}
diff --git a/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseToken.cs b/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseToken.cs
index e9c0c7e3..6145e805 100644
--- a/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseToken.cs
+++ b/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseToken.cs
@@ -23,73 +23,54 @@ namespace FirebaseAdmin.Auth
///
public sealed class FirebaseToken
{
+ internal FirebaseToken(FirebaseTokenArgs args)
+ {
+ this.Issuer = args.Issuer;
+ this.Subject = args.Subject;
+ this.Audience = args.Audience;
+ this.ExpirationTimeSeconds = args.ExpirationTimeSeconds;
+ this.IssuedAtTimeSeconds = args.IssuedAtTimeSeconds;
+ this.Uid = args.Subject;
+ this.Claims = args.Claims;
+ }
+
///
- /// The issuer claim that identifies the principal that issued the JWT.
+ /// Gets the issuer claim that identifies the principal that issued the JWT.
///
public string Issuer { get; private set; }
///
- /// The subject claim identifying the principal that is the subject of the JWT.
+ /// Gets the subject claim identifying the principal that is the subject of the JWT.
///
public string Subject { get; private set; }
///
- /// The audience claim that identifies the audience that the JWT is intended for.
+ /// Gets the audience claim that identifies the audience that the JWT is intended for.
///
public string Audience { get; private set; }
///
- /// The expiration time claim that identifies the expiration time (in seconds)
+ /// Gets the expiration time claim that identifies the expiration time (in seconds)
/// on or after which the token MUST NOT be accepted for processing.
///
public long ExpirationTimeSeconds { get; private set; }
///
- /// The issued at claim that identifies the time (in seconds) at which the JWT was issued.
+ /// Gets the issued at claim that identifies the time (in seconds) at which the JWT was
+ /// issued.
///
public long IssuedAtTimeSeconds { get; private set; }
-
+
///
- /// User ID of the user to which this ID token belongs. This is same as Subject.
+ /// Gets the User ID of the user to which this ID token belongs. This is same as
+ /// .
///
public string Uid { get; private set; }
///
- /// A read-only dictionary of all other claims present in the JWT. This can be used to
+ /// Gets all other claims present in the JWT as a readonly dictionary. This can be used to
/// access custom claims of the token.
///
public IReadOnlyDictionary Claims { get; private set; }
-
- internal FirebaseToken(FirebaseTokenArgs args)
- {
- Issuer = args.Issuer;
- Subject = args.Subject;
- Audience = args.Audience;
- ExpirationTimeSeconds = args.ExpirationTimeSeconds;
- IssuedAtTimeSeconds = args.IssuedAtTimeSeconds;
- Uid = args.Subject;
- Claims = args.Claims;
- }
- }
-
- internal sealed class FirebaseTokenArgs
- {
- [JsonProperty("iss")]
- public string Issuer { get; set; }
-
- [JsonProperty("sub")]
- public string Subject { get; set; }
-
- [JsonProperty("aud")]
- public string Audience { get; set; }
-
- [JsonProperty("exp")]
- public long ExpirationTimeSeconds { get; set; }
-
- [JsonProperty("iat")]
- public long IssuedAtTimeSeconds { get; set; }
-
- [JsonIgnore]
- public IReadOnlyDictionary Claims { get; set; }
}
}
diff --git a/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseTokenArgs.cs b/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseTokenArgs.cs
new file mode 100644
index 00000000..027feb6b
--- /dev/null
+++ b/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseTokenArgs.cs
@@ -0,0 +1,40 @@
+// Copyright 2018, Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Collections.Generic;
+using Newtonsoft.Json;
+
+namespace FirebaseAdmin.Auth
+{
+ internal sealed class FirebaseTokenArgs
+ {
+ [JsonProperty("iss")]
+ public string Issuer { get; set; }
+
+ [JsonProperty("sub")]
+ public string Subject { get; set; }
+
+ [JsonProperty("aud")]
+ public string Audience { get; set; }
+
+ [JsonProperty("exp")]
+ public long ExpirationTimeSeconds { get; set; }
+
+ [JsonProperty("iat")]
+ public long IssuedAtTimeSeconds { get; set; }
+
+ [JsonIgnore]
+ public IReadOnlyDictionary Claims { get; set; }
+ }
+}
diff --git a/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseTokenFactory.cs b/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseTokenFactory.cs
index 79e3b4ab..411c97fa 100644
--- a/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseTokenFactory.cs
+++ b/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseTokenFactory.cs
@@ -15,46 +15,86 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
+using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
-using System.Threading.Tasks;
-using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
using Google.Apis.Auth;
-using Google.Apis.Http;
using Google.Apis.Auth.OAuth2;
+using Google.Apis.Http;
using Google.Apis.Util;
-[assembly: InternalsVisibleToAttribute("FirebaseAdmin.Tests,PublicKey="+
-"002400000480000094000000060200000024000052534131000400000100010081328559eaab41"+
-"055b84af73469863499d81625dcbba8d8decb298b69e0f783a0958cf471fd4f76327b85a7d4b02"+
-"3003684e85e61cf15f13150008c81f0b75a252673028e530ea95d0c581378da8c6846526ab9597"+
-"4c6d0bc66d2462b51af69968a0e25114bde8811e0d6ee1dc22d4a59eee6a8bba4712cba839652f"+
+[assembly: InternalsVisibleToAttribute("FirebaseAdmin.Tests,PublicKey=" +
+"002400000480000094000000060200000024000052534131000400000100010081328559eaab41" +
+"055b84af73469863499d81625dcbba8d8decb298b69e0f783a0958cf471fd4f76327b85a7d4b02" +
+"3003684e85e61cf15f13150008c81f0b75a252673028e530ea95d0c581378da8c6846526ab9597" +
+"4c6d0bc66d2462b51af69968a0e25114bde8811e0d6ee1dc22d4a59eee6a8bba4712cba839652f" +
"badddb9c")]
namespace FirebaseAdmin.Auth
{
///
/// A helper class that creates Firebase custom tokens.
///
- internal class FirebaseTokenFactory: IDisposable
+ internal class FirebaseTokenFactory : IDisposable
{
public const string FirebaseAudience = "https://identitytoolkit.googleapis.com/"
+ "google.identity.identitytoolkit.v1.IdentityToolkit";
-
+
public const int TokenDurationSeconds = 3600;
public static readonly DateTime UnixEpoch = new DateTime(
1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+
public static readonly ImmutableList ReservedClaims = ImmutableList.Create(
- "acr", "amr", "at_hash", "aud", "auth_time", "azp", "cnf", "c_hash",
- "exp", "firebase", "iat", "iss", "jti", "nbf", "nonce", "sub"
- );
+ "acr",
+ "amr",
+ "at_hash",
+ "aud",
+ "auth_time",
+ "azp",
+ "cnf",
+ "c_hash",
+ "exp",
+ "firebase",
+ "iat",
+ "iss",
+ "jti",
+ "nbf",
+ "nonce",
+ "sub");
- private readonly ISigner _signer;
- private readonly IClock _clock;
+ private readonly ISigner signer;
+ private readonly IClock clock;
public FirebaseTokenFactory(ISigner signer, IClock clock)
{
- _signer = signer.ThrowIfNull(nameof(signer));
- _clock = clock.ThrowIfNull(nameof(clock));
+ this.signer = signer.ThrowIfNull(nameof(signer));
+ this.clock = clock.ThrowIfNull(nameof(clock));
+ }
+
+ public static FirebaseTokenFactory Create(FirebaseApp app)
+ {
+ ISigner signer = null;
+ var serviceAccount = app.Options.Credential.ToServiceAccountCredential();
+ if (serviceAccount != null)
+ {
+ // If the app was initialized with a service account, use it to sign
+ // tokens locally.
+ signer = new ServiceAccountSigner(serviceAccount);
+ }
+ else if (string.IsNullOrEmpty(app.Options.ServiceAccountId))
+ {
+ // If no service account ID is specified, attempt to discover one and invoke the
+ // IAM service with it.
+ signer = new IAMSigner(new HttpClientFactory(), app.Options.Credential);
+ }
+ else
+ {
+ // If a service account ID is specified, invoke the IAM service with it.
+ signer = new FixedAccountIAMSigner(
+ new HttpClientFactory(), app.Options.Credential, app.Options.ServiceAccountId);
+ }
+
+ return new FirebaseTokenFactory(signer, SystemClock.Default);
}
public async Task CreateCustomTokenAsync(
@@ -70,6 +110,7 @@ public async Task CreateCustomTokenAsync(
{
throw new ArgumentException("uid must not be longer than 128 characters");
}
+
if (developerClaims != null)
{
foreach (var entry in developerClaims)
@@ -85,11 +126,11 @@ public async Task CreateCustomTokenAsync(
var header = new JsonWebSignature.Header()
{
Algorithm = "RS256",
- Type = "JWT"
+ Type = "JWT",
};
-
- var issued = (int)(_clock.UtcNow - UnixEpoch).TotalSeconds;
- var keyId = await _signer.GetKeyIdAsync(cancellationToken).ConfigureAwait(false);
+
+ var issued = (int)(this.clock.UtcNow - UnixEpoch).TotalSeconds;
+ var keyId = await this.signer.GetKeyIdAsync(cancellationToken).ConfigureAwait(false);
var payload = new CustomTokenPayload()
{
Uid = uid,
@@ -97,53 +138,30 @@ public async Task CreateCustomTokenAsync(
Subject = keyId,
Audience = FirebaseAudience,
IssuedAtTimeSeconds = issued,
- ExpirationTimeSeconds = issued + TokenDurationSeconds,
+ ExpirationTimeSeconds = issued + TokenDurationSeconds,
};
+
if (developerClaims != null && developerClaims.Count > 0)
{
payload.Claims = developerClaims;
}
+
return await JwtUtils.CreateSignedJwtAsync(
- header, payload, _signer, cancellationToken).ConfigureAwait(false);
+ header, payload, this.signer, cancellationToken).ConfigureAwait(false);
}
public void Dispose()
{
- _signer.Dispose();
+ this.signer.Dispose();
}
- public static FirebaseTokenFactory Create(FirebaseApp app)
+ internal class CustomTokenPayload : JsonWebToken.Payload
{
- ISigner signer = null;
- var serviceAccount = app.Options.Credential.ToServiceAccountCredential();
- if (serviceAccount != null)
- {
- // If the app was initialized with a service account, use it to sign
- // tokens locally.
- signer = new ServiceAccountSigner(serviceAccount);
- }
- else if (string.IsNullOrEmpty(app.Options.ServiceAccountId))
- {
- // If no service account ID is specified, attempt to discover one and invoke the
- // IAM service with it.
- signer = new IAMSigner(new HttpClientFactory(), app.Options.Credential);
- }
- else
- {
- // If a service account ID is specified, invoke the IAM service with it.
- signer = new FixedAccountIAMSigner(
- new HttpClientFactory(), app.Options.Credential, app.Options.ServiceAccountId);
- }
- return new FirebaseTokenFactory(signer, SystemClock.Default);
- }
- }
+ [Newtonsoft.Json.JsonPropertyAttribute("uid")]
+ public string Uid { get; set; }
- internal class CustomTokenPayload: JsonWebToken.Payload
- {
- [Newtonsoft.Json.JsonPropertyAttribute("uid")]
- public string Uid { get; set; }
-
- [Newtonsoft.Json.JsonPropertyAttribute("claims")]
- public IDictionary Claims { get; set; }
+ [Newtonsoft.Json.JsonPropertyAttribute("claims")]
+ public IDictionary Claims { get; set; }
+ }
}
}
diff --git a/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseTokenVerifier.cs b/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseTokenVerifier.cs
index e96b3349..172fa03e 100644
--- a/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseTokenVerifier.cs
+++ b/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseTokenVerifier.cs
@@ -15,6 +15,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
@@ -34,7 +35,7 @@ internal sealed class FirebaseTokenVerifier
private const string IdTokenCertUrl = "https://www.googleapis.com/robot/v1/metadata/x509/"
+ "securetoken@system.gserviceaccount.com";
- private const string FirebaseAudience ="https://identitytoolkit.googleapis.com/"
+ private const string FirebaseAudience = "https://identitytoolkit.googleapis.com/"
+ "google.identity.identitytoolkit.v1.IdentityToolkit";
// See http://oid-info.com/get/2.16.840.1.101.3.4.2.1
@@ -43,117 +44,146 @@ internal sealed class FirebaseTokenVerifier
private static readonly IReadOnlyList StandardClaims =
ImmutableList.Create("iss", "aud", "exp", "iat", "sub", "uid");
- public string ProjectId { get; }
- private readonly string _shortName;
- private readonly string _articledShortName;
- private readonly string _operation;
- private readonly string _url;
- private readonly string _issuer;
- private readonly IClock _clock;
- private readonly IPublicKeySource _keySource;
-
- public FirebaseTokenVerifier(FirebaseTokenVerifierArgs args)
+ private readonly string shortName;
+ private readonly string articledShortName;
+ private readonly string operation;
+ private readonly string url;
+ private readonly string issuer;
+ private readonly IClock clock;
+ private readonly IPublicKeySource keySource;
+
+ internal FirebaseTokenVerifier(FirebaseTokenVerifierArgs args)
{
- ProjectId = args.ProjectId.ThrowIfNullOrEmpty(nameof(args.ProjectId));
- _shortName = args.ShortName.ThrowIfNullOrEmpty(nameof(args.ShortName));
- _operation = args.Operation.ThrowIfNullOrEmpty(nameof(args.Operation));
- _url = args.Url.ThrowIfNullOrEmpty(nameof(args.Url));
- _issuer = args.Issuer.ThrowIfNullOrEmpty(nameof(args.Issuer));
- _clock = args.Clock.ThrowIfNull(nameof(args.Clock));
- _keySource = args.PublicKeySource.ThrowIfNull(nameof(args.PublicKeySource));
- if ("aeiou".Contains(_shortName.ToLower().Substring(0, 1)))
+ this.ProjectId = args.ProjectId.ThrowIfNullOrEmpty(nameof(args.ProjectId));
+ this.shortName = args.ShortName.ThrowIfNullOrEmpty(nameof(args.ShortName));
+ this.operation = args.Operation.ThrowIfNullOrEmpty(nameof(args.Operation));
+ this.url = args.Url.ThrowIfNullOrEmpty(nameof(args.Url));
+ this.issuer = args.Issuer.ThrowIfNullOrEmpty(nameof(args.Issuer));
+ this.clock = args.Clock.ThrowIfNull(nameof(args.Clock));
+ this.keySource = args.PublicKeySource.ThrowIfNull(nameof(args.PublicKeySource));
+ if ("aeiou".Contains(this.shortName.ToLower().Substring(0, 1)))
{
- _articledShortName = $"an {_shortName}";
+ this.articledShortName = $"an {this.shortName}";
}
else
{
- _articledShortName = $"a {_shortName}";
+ this.articledShortName = $"a {this.shortName}";
}
}
- public async Task VerifyTokenAsync(
+ public string ProjectId { get; }
+
+ internal static FirebaseTokenVerifier CreateIDTokenVerifier(FirebaseApp app)
+ {
+ var projectId = app.GetProjectId();
+ if (string.IsNullOrEmpty(projectId))
+ {
+ throw new ArgumentException(
+ "Must initialize FirebaseApp with a project ID to verify ID tokens.");
+ }
+
+ var keySource = new HttpPublicKeySource(
+ IdTokenCertUrl, SystemClock.Default, new HttpClientFactory());
+ var args = new FirebaseTokenVerifierArgs()
+ {
+ ProjectId = projectId,
+ ShortName = "ID token",
+ Operation = "VerifyIdTokenAsync()",
+ Url = "https://firebase.google.com/docs/auth/admin/verify-id-tokens",
+ Issuer = "https://securetoken.google.com/",
+ Clock = SystemClock.Default,
+ PublicKeySource = keySource,
+ };
+
+ return new FirebaseTokenVerifier(args);
+ }
+
+ internal async Task VerifyTokenAsync(
string token, CancellationToken cancellationToken = default(CancellationToken))
{
if (string.IsNullOrEmpty(token))
{
- throw new ArgumentException($"{_shortName} must not be null or empty.");
+ throw new ArgumentException($"{this.shortName} must not be null or empty.");
}
+
string[] segments = token.Split('.');
if (segments.Length != 3)
{
- throw new FirebaseException($"Incorrect number of segments in ${_shortName}.");
+ throw new FirebaseException($"Incorrect number of segments in ${this.shortName}.");
}
var header = JwtUtils.Decode(segments[0]);
var payload = JwtUtils.Decode(segments[1]);
- var projectIdMessage = $"Make sure the {_shortName} comes from the same Firebase "
+ var projectIdMessage = $"Make sure the {this.shortName} comes from the same Firebase "
+ "project as the credential used to initialize this SDK.";
- var verifyTokenMessage = $"See {_url} for details on how to retrieve a value "
- + $"{_shortName}.";
- var issuer = _issuer + ProjectId;
+ var verifyTokenMessage = $"See {this.url} for details on how to retrieve a value "
+ + $"{this.shortName}.";
+ var issuer = this.issuer + this.ProjectId;
string error = null;
if (string.IsNullOrEmpty(header.KeyId))
{
- if (FirebaseAudience == payload.Audience)
+ if (payload.Audience == FirebaseAudience)
{
- error = $"{_operation} expects {_articledShortName}, but was given a custom "
+ error = $"{this.operation} expects {this.articledShortName}, but was given a custom "
+ "token.";
}
else if (header.Algorithm == "HS256")
{
- error = $"{_operation} expects {_articledShortName}, but was given a legacy "
+ error = $"{this.operation} expects {this.articledShortName}, but was given a legacy "
+ "custom token.";
}
else
{
- error = $"Firebase {_shortName} has no 'kid' claim.";
+ error = $"Firebase {this.shortName} has no 'kid' claim.";
}
}
else if (header.Algorithm != "RS256")
{
- error = $"Firebase {_shortName} has incorrect algorithm. Expected RS256 but got "
+ error = $"Firebase {this.shortName} has incorrect algorithm. Expected RS256 but got "
+ $"{header.Algorithm}. {verifyTokenMessage}";
}
- else if (ProjectId != payload.Audience)
+ else if (this.ProjectId != payload.Audience)
{
- error = $"{_shortName} has incorrect audience (aud) claim. Expected {ProjectId} "
+ error = $"{this.shortName} has incorrect audience (aud) claim. Expected {this.ProjectId} "
+ $"but got {payload.Audience}. {projectIdMessage} {verifyTokenMessage}";
}
else if (payload.Issuer != issuer)
{
- error = $"{_shortName} has incorrect issuer (iss) claim. Expected {issuer} but "
+ error = $"{this.shortName} has incorrect issuer (iss) claim. Expected {issuer} but "
+ $"got {payload.Issuer}. {projectIdMessage} {verifyTokenMessage}";
}
- else if (payload.IssuedAtTimeSeconds > _clock.UnixTimestamp())
+ else if (payload.IssuedAtTimeSeconds > this.clock.UnixTimestamp())
{
- error = $"Firebase {_shortName} issued at future timestamp";
+ error = $"Firebase {this.shortName} issued at future timestamp";
}
- else if (payload.ExpirationTimeSeconds < _clock.UnixTimestamp())
+ else if (payload.ExpirationTimeSeconds < this.clock.UnixTimestamp())
{
- error = $"Firebase {_shortName} expired at {payload.ExpirationTimeSeconds}";
+ error = $"Firebase {this.shortName} expired at {payload.ExpirationTimeSeconds}";
}
else if (string.IsNullOrEmpty(payload.Subject))
{
- error = $"Firebase {_shortName} has no or empty subject (sub) claim.";
+ error = $"Firebase {this.shortName} has no or empty subject (sub) claim.";
}
else if (payload.Subject.Length > 128)
{
- error = $"Firebase {_shortName} has a subject claim longer than 128 characters.";
+ error = $"Firebase {this.shortName} has a subject claim longer than 128 characters.";
}
-
+
if (error != null)
{
throw new FirebaseException(error);
}
- await VerifySignatureAsync(segments, header.KeyId, cancellationToken)
+ await this.VerifySignatureAsync(segments, header.KeyId, cancellationToken)
.ConfigureAwait(false);
var allClaims = JwtUtils.Decode>(segments[1]);
+
// Remove standard claims, so that only custom claims would remain.
foreach (var claim in StandardClaims)
{
allClaims.Remove(claim);
}
+
payload.Claims = allClaims.ToImmutableDictionary();
return new FirebaseToken(payload);
}
@@ -162,6 +192,14 @@ await VerifySignatureAsync(segments, header.KeyId, cancellationToken)
/// Verifies the integrity of a JWT by validating its signature. The JWT must be specified
/// as an array of three segments (header, body and signature).
///
+ [SuppressMessage(
+ "StyleCop.Analyzers",
+ "SA1009:ClosingParenthesisMustBeSpacedCorrectly",
+ Justification = "Use of directives.")]
+ [SuppressMessage(
+ "StyleCop.Analyzers",
+ "SA1111:ClosingParenthesisMustBeOnLineOfLastParameter",
+ Justification = "Use of directives.")]
private async Task VerifySignatureAsync(
string[] segments, string keyId, CancellationToken cancellationToken)
{
@@ -171,15 +209,16 @@ private async Task VerifySignatureAsync(
hash = hashAlg.ComputeHash(
Encoding.ASCII.GetBytes($"{segments[0]}.{segments[1]}"));
}
+
var signature = JwtUtils.Base64DecodeToBytes(segments[2]);
- var keys = await _keySource.GetPublicKeysAsync(cancellationToken)
+ var keys = await this.keySource.GetPublicKeysAsync(cancellationToken)
.ConfigureAwait(false);
var verified = keys.Any(key =>
#if NETSTANDARD1_5 || NETSTANDARD2_0
key.Id == keyId && key.RSA.VerifyHash(
hash, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)
#elif NET45
- key.Id == keyId &&
+ key.Id == keyId &&
((RSACryptoServiceProvider) key.RSA).VerifyHash(hash, Sha256Oid, signature)
#else
#error Unsupported target
@@ -187,42 +226,8 @@ private async Task VerifySignatureAsync(
);
if (!verified)
{
- throw new FirebaseException($"Failed to verify {_shortName} signature.");
+ throw new FirebaseException($"Failed to verify {this.shortName} signature.");
}
}
-
- internal static FirebaseTokenVerifier CreateIDTokenVerifier(FirebaseApp app)
- {
- var projectId = app.GetProjectId();
- if (string.IsNullOrEmpty(projectId))
- {
- throw new ArgumentException(
- "Must initialize FirebaseApp with a project ID to verify ID tokens.");
- }
- var keySource = new HttpPublicKeySource(
- IdTokenCertUrl, SystemClock.Default, new HttpClientFactory());
- var args = new FirebaseTokenVerifierArgs()
- {
- ProjectId = projectId,
- ShortName = "ID token",
- Operation = "VerifyIdTokenAsync()",
- Url = "https://firebase.google.com/docs/auth/admin/verify-id-tokens",
- Issuer = "https://securetoken.google.com/",
- Clock = SystemClock.Default,
- PublicKeySource = keySource,
- };
- return new FirebaseTokenVerifier(args);
- }
- }
-
- internal sealed class FirebaseTokenVerifierArgs
- {
- public string ProjectId { get; set; }
- public string ShortName { get; set; }
- public string Operation { get; set; }
- public string Url { get; set; }
- public string Issuer { get; set; }
- public IClock Clock { get; set; }
- public IPublicKeySource PublicKeySource { get; set; }
}
}
diff --git a/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseTokenVerifierArgs.cs b/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseTokenVerifierArgs.cs
new file mode 100644
index 00000000..d79524b7
--- /dev/null
+++ b/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseTokenVerifierArgs.cs
@@ -0,0 +1,35 @@
+// Copyright 2018, Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using Google.Apis.Util;
+
+namespace FirebaseAdmin.Auth
+{
+ internal sealed class FirebaseTokenVerifierArgs
+ {
+ public string ProjectId { get; set; }
+
+ public string ShortName { get; set; }
+
+ public string Operation { get; set; }
+
+ public string Url { get; set; }
+
+ public string Issuer { get; set; }
+
+ public IClock Clock { get; set; }
+
+ public IPublicKeySource PublicKeySource { get; set; }
+ }
+}
diff --git a/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseUserManager.cs b/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseUserManager.cs
index 3a3e86fc..805d6347 100644
--- a/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseUserManager.cs
+++ b/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseUserManager.cs
@@ -12,32 +12,51 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using Google.Apis.Auth.OAuth2;
-using Google.Apis.Http;
-using Newtonsoft.Json.Linq;
using System;
using System.Net.Http;
using System.Threading.Tasks;
+using Google.Apis.Auth.OAuth2;
+using Google.Apis.Http;
+using Newtonsoft.Json.Linq;
namespace FirebaseAdmin.Auth
{
///
- /// FirebaseUserManager provides methods for interacting with the
+ /// FirebaseUserManager provides methods for interacting with the
///
- /// Google Identity Toolkit via its REST API. This class does not hold any mutable state,
+ /// Google Identity Toolkit via its REST API. This class does not hold any mutable state,
/// and is thread safe.
///
internal class FirebaseUserManager : IDisposable
{
- private const string ID_TOOLKIT_URL = "https://identitytoolkit.googleapis.com/v1/projects/{0}";
+ private const string IdTooklitUrl = "https://identitytoolkit.googleapis.com/v1/projects/{0}";
- private readonly ConfigurableHttpClient _httpClient;
- private readonly string _baseUrl;
+ private readonly ConfigurableHttpClient httpClient;
+ private readonly string baseUrl;
internal FirebaseUserManager(FirebaseUserManagerArgs args)
{
- _httpClient = args.ClientFactory.CreateAuthorizedHttpClient(args.Credential);
- _baseUrl = string.Format(ID_TOOLKIT_URL, args.ProjectId);
+ this.httpClient = args.ClientFactory.CreateAuthorizedHttpClient(args.Credential);
+ this.baseUrl = string.Format(IdTooklitUrl, args.ProjectId);
+ }
+
+ public static FirebaseUserManager Create(FirebaseApp app)
+ {
+ var projectId = app.GetProjectId();
+ if (string.IsNullOrEmpty(projectId))
+ {
+ throw new ArgumentException(
+ "Must initialize FirebaseApp with a project ID to manage users.");
+ }
+
+ var args = new FirebaseUserManagerArgs
+ {
+ ClientFactory = new HttpClientFactory(),
+ Credential = app.Options.Credential,
+ ProjectId = projectId,
+ };
+
+ return new FirebaseUserManager(args);
}
///
@@ -48,7 +67,7 @@ internal FirebaseUserManager(FirebaseUserManagerArgs args)
public async Task UpdateUserAsync(UserRecord user)
{
var updatePath = "/accounts:update";
- var resopnse = await PostAsync(updatePath, user);
+ var resopnse = await this.PostAsync(updatePath, user);
try
{
@@ -64,13 +83,18 @@ public async Task UpdateUserAsync(UserRecord user)
}
}
+ public void Dispose()
+ {
+ this.httpClient.Dispose();
+ }
+
private async Task PostAsync(string path, UserRecord user)
{
- var requestUri = $"{_baseUrl}{path}";
+ var requestUri = $"{this.baseUrl}{path}";
HttpResponseMessage response = null;
try
{
- response = await _httpClient.PostJsonAsync(requestUri, user, default);
+ response = await this.httpClient.PostJsonAsync(requestUri, user, default);
var json = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
@@ -90,36 +114,5 @@ private async Task PostAsync(string path, UserRecord user)
throw new FirebaseException("Error while calling Firebase Auth service", e);
}
}
-
- internal static FirebaseUserManager Create(FirebaseApp app)
- {
- var projectId = app.GetProjectId();
- if (string.IsNullOrEmpty(projectId))
- {
- throw new ArgumentException(
- "Must initialize FirebaseApp with a project ID to manage users.");
- }
-
- var args = new FirebaseUserManagerArgs
- {
- ClientFactory = new HttpClientFactory(),
- Credential = app.Options.Credential,
- ProjectId = projectId,
- };
-
- return new FirebaseUserManager(args);
- }
-
- public void Dispose()
- {
- _httpClient.Dispose();
- }
- }
-
- internal sealed class FirebaseUserManagerArgs
- {
- public HttpClientFactory ClientFactory { get; set; }
- public GoogleCredential Credential { get; set; }
- public string ProjectId { get; set; }
}
}
diff --git a/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseUserManagerArgs.cs b/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseUserManagerArgs.cs
new file mode 100644
index 00000000..034f4ffc
--- /dev/null
+++ b/FirebaseAdmin/FirebaseAdmin/Auth/FirebaseUserManagerArgs.cs
@@ -0,0 +1,28 @@
+// Copyright 2019, Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using Google.Apis.Auth.OAuth2;
+using Google.Apis.Http;
+
+namespace FirebaseAdmin.Auth
+{
+ internal sealed class FirebaseUserManagerArgs
+ {
+ public HttpClientFactory ClientFactory { get; set; }
+
+ public GoogleCredential Credential { get; set; }
+
+ public string ProjectId { get; set; }
+ }
+}
diff --git a/FirebaseAdmin/FirebaseAdmin/Auth/FixedAccountIAMSigner.cs b/FirebaseAdmin/FirebaseAdmin/Auth/FixedAccountIAMSigner.cs
new file mode 100644
index 00000000..8398d9fb
--- /dev/null
+++ b/FirebaseAdmin/FirebaseAdmin/Auth/FixedAccountIAMSigner.cs
@@ -0,0 +1,45 @@
+// Copyright 2018, Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Threading;
+using System.Threading.Tasks;
+using Google.Apis.Auth.OAuth2;
+using Google.Apis.Http;
+using Google.Apis.Util;
+
+namespace FirebaseAdmin.Auth
+{
+ ///
+ /// An implementation that uses the IAM service to sign data. Unlike
+ /// this class does not attempt to auto discover a service account ID.
+ /// Insterad it must be initialized with a fixed service account ID string.
+ ///
+ internal sealed class FixedAccountIAMSigner : IAMSigner
+ {
+ private readonly string keyId;
+
+ public FixedAccountIAMSigner(
+ HttpClientFactory clientFactory, GoogleCredential credential, string keyId)
+ : base(clientFactory, credential)
+ {
+ this.keyId = keyId.ThrowIfNullOrEmpty(nameof(keyId));
+ }
+
+ public override Task GetKeyIdAsync(
+ CancellationToken cancellationToken = default(CancellationToken))
+ {
+ return Task.FromResult(this.keyId);
+ }
+ }
+}
diff --git a/FirebaseAdmin/FirebaseAdmin/Auth/HttpPublicKeySource.cs b/FirebaseAdmin/FirebaseAdmin/Auth/HttpPublicKeySource.cs
index ffbbd9ff..6851d190 100644
--- a/FirebaseAdmin/FirebaseAdmin/Auth/HttpPublicKeySource.cs
+++ b/FirebaseAdmin/FirebaseAdmin/Auth/HttpPublicKeySource.cs
@@ -21,8 +21,8 @@
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using Google.Apis.Json;
using Google.Apis.Http;
+using Google.Apis.Json;
using Google.Apis.Util;
#if NETSTANDARD1_5 || NETSTANDARD2_0
@@ -40,7 +40,7 @@ namespace FirebaseAdmin.Auth
/// HTTP server. Retrieved keys are cached in memory according to the HTTP cache-control
/// directive.
///
- internal sealed class HttpPublicKeySource: IPublicKeySource
+ internal sealed class HttpPublicKeySource : IPublicKeySource
{
// Default clock skew used by most GCP libraries. This interval is subtracted from the
// cache expiry time, before any expiration checks. This helps correct for minor
@@ -48,44 +48,44 @@ internal sealed class HttpPublicKeySource: IPublicKeySource
// pre-emptively refreshed instead of waiting until the last second.
private static readonly TimeSpan ClockSkew = new TimeSpan(hours: 0, minutes: 5, seconds: 0);
- private readonly string _certUrl;
- private IReadOnlyList _cachedKeys;
- private DateTime _expirationTime;
- private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1);
- private readonly IClock _clock;
- private readonly HttpClientFactory _clientFactory;
+ private readonly SemaphoreSlim cacheLock = new SemaphoreSlim(1, 1);
+ private readonly string certUrl;
+ private readonly IClock clock;
+ private readonly HttpClientFactory clientFactory;
+ private DateTime expirationTime;
+ private IReadOnlyList cachedKeys;
public HttpPublicKeySource(string certUrl, IClock clock, HttpClientFactory clientFactory)
{
- _certUrl = certUrl.ThrowIfNullOrEmpty(nameof(certUrl));
- _clock = clock.ThrowIfNull(nameof(clock));
- _clientFactory = clientFactory.ThrowIfNull(nameof(clientFactory));
- _expirationTime = clock.UtcNow;
+ this.certUrl = certUrl.ThrowIfNullOrEmpty(nameof(certUrl));
+ this.clock = clock.ThrowIfNull(nameof(clock));
+ this.clientFactory = clientFactory.ThrowIfNull(nameof(clientFactory));
+ this.expirationTime = clock.UtcNow;
}
public async Task> GetPublicKeysAsync(
CancellationToken cancellationToken = default(CancellationToken))
{
- if (_cachedKeys == null || _clock.UtcNow >= _expirationTime)
+ if (this.cachedKeys == null || this.clock.UtcNow >= this.expirationTime)
{
- await _lock.WaitAsync(cancellationToken).ConfigureAwait(false);
+ await this.cacheLock.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
- var now = _clock.UtcNow;
- if (_cachedKeys == null || now >= _expirationTime)
+ var now = this.clock.UtcNow;
+ if (this.cachedKeys == null || now >= this.expirationTime)
{
- using (var httpClient = _clientFactory.CreateDefaultHttpClient())
+ using (var httpClient = this.clientFactory.CreateDefaultHttpClient())
{
- var response = await httpClient.GetAsync(_certUrl, cancellationToken)
+ var response = await httpClient.GetAsync(this.certUrl, cancellationToken)
.ConfigureAwait(false);
response.EnsureSuccessStatusCode();
- _cachedKeys = ParseKeys(await response.Content.ReadAsStringAsync()
+ this.cachedKeys = this.ParseKeys(await response.Content.ReadAsStringAsync()
.ConfigureAwait(false));
var cacheControl = response.Headers.CacheControl;
if (cacheControl?.MaxAge != null)
{
- _expirationTime = now.Add(cacheControl.MaxAge.Value)
+ this.expirationTime = now.Add(cacheControl.MaxAge.Value)
.Subtract(ClockSkew);
}
}
@@ -97,11 +97,11 @@ public async Task> GetPublicKeysAsync(
}
finally
{
- _lock.Release();
+ this.cacheLock.Release();
}
}
- return _cachedKeys;
+ return this.cachedKeys;
}
private IReadOnlyList ParseKeys(string json)
@@ -112,6 +112,7 @@ private IReadOnlyList ParseKeys(string json)
{
throw new InvalidDataException("No public keys present in the response.");
}
+
var builder = ImmutableList.CreateBuilder();
foreach (var entry in rawKeys)
{
@@ -126,6 +127,7 @@ private IReadOnlyList ParseKeys(string json)
#endif
builder.Add(new PublicKey(entry.Key, rsa));
}
+
return builder.ToImmutableList();
}
}
diff --git a/FirebaseAdmin/FirebaseAdmin/Auth/IAMSigner.cs b/FirebaseAdmin/FirebaseAdmin/Auth/IAMSigner.cs
index dec55cd4..f7d810bd 100644
--- a/FirebaseAdmin/FirebaseAdmin/Auth/IAMSigner.cs
+++ b/FirebaseAdmin/FirebaseAdmin/Auth/IAMSigner.cs
@@ -36,16 +36,17 @@ internal class IAMSigner : ISigner
{
private const string SignBlobUrl =
"https://iam.googleapis.com/v1/projects/-/serviceAccounts/{0}:signBlob";
- private const string MetadataServerUrl =
+
+ private const string MetadataServerUrl =
"http://metadata/computeMetadata/v1/instance/service-accounts/default/email";
- private readonly ConfigurableHttpClient _httpClient;
- private readonly Lazy> _keyId;
+ private readonly ConfigurableHttpClient httpClient;
+ private readonly Lazy> keyId;
public IAMSigner(HttpClientFactory clientFactory, GoogleCredential credential)
{
- _httpClient = clientFactory.CreateAuthorizedHttpClient(credential);
- _keyId = new Lazy>(
+ this.httpClient = clientFactory.CreateAuthorizedHttpClient(credential);
+ this.keyId = new Lazy>(
async () => await DiscoverServiceAccountIdAsync(clientFactory)
.ConfigureAwait(false), true);
}
@@ -53,18 +54,19 @@ public IAMSigner(HttpClientFactory clientFactory, GoogleCredential credential)
public async Task SignDataAsync(
byte[] data, CancellationToken cancellationToken = default(CancellationToken))
{
- var keyId = await GetKeyIdAsync(cancellationToken).ConfigureAwait(false);
+ var keyId = await this.GetKeyIdAsync(cancellationToken).ConfigureAwait(false);
var url = string.Format(SignBlobUrl, keyId);
var request = new SignBlobRequest
{
BytesToSign = Convert.ToBase64String(data),
};
+
try
{
- var response = await _httpClient.PostJsonAsync(url, request, cancellationToken)
+ var response = await this.httpClient.PostJsonAsync(url, request, cancellationToken)
.ConfigureAwait(false);
var json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
- ThrowIfError(response, json);
+ this.ThrowIfError(response, json);
var parsed = NewtonsoftJsonSerializer.Instance.Deserialize(json);
return Convert.FromBase64String(parsed.Signature);
}
@@ -74,34 +76,12 @@ public async Task SignDataAsync(
}
}
- private void ThrowIfError(HttpResponseMessage response, string content)
- {
- if (response.IsSuccessStatusCode)
- {
- return;
- }
- string error = null;
- try
- {
- var result = NewtonsoftJsonSerializer.Instance.Deserialize(content);
- error = result?.Error.Message;
- }
- catch (Exception) {} // Ignore any errors encountered while parsing the originl error.
- if (string.IsNullOrEmpty(error))
- {
- error = "Response status code does not indicate success: "
- + $"{(int) response.StatusCode} ({response.StatusCode})"
- + $"{Environment.NewLine}{content}";
- }
- throw new FirebaseException(error);
- }
-
public virtual async Task GetKeyIdAsync(
CancellationToken cancellationToken = default(CancellationToken))
{
try
{
- return await _keyId.Value.ConfigureAwait(false);
+ return await this.keyId.Value.ConfigureAwait(false);
}
catch (Exception e)
{
@@ -114,6 +94,11 @@ public virtual async Task GetKeyIdAsync(
}
}
+ public void Dispose()
+ {
+ this.httpClient.Dispose();
+ }
+
private static async Task DiscoverServiceAccountIdAsync(
HttpClientFactory clientFactory)
{
@@ -124,67 +109,68 @@ private static async Task DiscoverServiceAccountIdAsync(
}
}
- public void Dispose()
+ private void ThrowIfError(HttpResponseMessage response, string content)
{
- _httpClient.Dispose();
- }
- }
+ if (response.IsSuccessStatusCode)
+ {
+ return;
+ }
- ///
- /// Represents the sign request sent to the remote IAM service.
- ///
- internal class SignBlobRequest
- {
- [Newtonsoft.Json.JsonProperty("bytesToSign")]
- public string BytesToSign { get; set; }
- }
+ string error = null;
+ try
+ {
+ var result = NewtonsoftJsonSerializer.Instance.Deserialize(content);
+ error = result?.Error.Message;
+ }
+ catch (Exception)
+ {
+ // Ignore any errors encountered while parsing the originl error.
+ }
- ///
- /// Represents the sign response sent by the remote IAM service.
- ///
- internal class SignBlobResponse
- {
- [Newtonsoft.Json.JsonProperty("signature")]
- public string Signature { get; set; }
- }
+ if (string.IsNullOrEmpty(error))
+ {
+ error = "Response status code does not indicate success: "
+ + $"{(int)response.StatusCode} ({response.StatusCode})"
+ + $"{Environment.NewLine}{content}";
+ }
- ///
- /// Represents an error response sent by the remote IAM service.
- ///
- internal class SignBlobError
- {
- [Newtonsoft.Json.JsonProperty("error")]
- public SignBlobErrorDetail Error { get; set; }
- }
+ throw new FirebaseException(error);
+ }
- ///
- /// Represents the error details embedded in an IAM error response.
- ///
- internal class SignBlobErrorDetail
- {
- [Newtonsoft.Json.JsonProperty("message")]
- public string Message { get; set; }
- }
+ ///
+ /// Represents the sign request sent to the remote IAM service.
+ ///
+ internal class SignBlobRequest
+ {
+ [Newtonsoft.Json.JsonProperty("bytesToSign")]
+ public string BytesToSign { get; set; }
+ }
- ///
- /// An implementation that uses the IAM service to sign data. Unlike
- /// this class does not attempt to auto discover a service account ID.
- /// Insterad it must be initialized with a fixed service account ID string.
- ///
- internal sealed class FixedAccountIAMSigner : IAMSigner
- {
- private readonly string _keyId;
+ ///
+ /// Represents the sign response sent by the remote IAM service.
+ ///
+ internal class SignBlobResponse
+ {
+ [Newtonsoft.Json.JsonProperty("signature")]
+ public string Signature { get; set; }
+ }
- public FixedAccountIAMSigner(HttpClientFactory clientFactory, GoogleCredential credential,
- string keyId): base(clientFactory, credential)
+ ///
+ /// Represents an error response sent by the remote IAM service.
+ ///
+ private class SignBlobError
{
- _keyId = keyId.ThrowIfNullOrEmpty(nameof(keyId));
+ [Newtonsoft.Json.JsonProperty("error")]
+ public SignBlobErrorDetail Error { get; set; }
}
- public override Task GetKeyIdAsync(
- CancellationToken cancellationToken = default(CancellationToken))
+ ///
+ /// Represents the error details embedded in an IAM error response.
+ ///
+ private class SignBlobErrorDetail
{
- return Task.FromResult(_keyId);
+ [Newtonsoft.Json.JsonProperty("message")]
+ public string Message { get; set; }
}
}
-}
\ No newline at end of file
+}
diff --git a/FirebaseAdmin/FirebaseAdmin/Auth/ISigner.cs b/FirebaseAdmin/FirebaseAdmin/Auth/ISigner.cs
index 2d63798e..dcacb8ee 100644
--- a/FirebaseAdmin/FirebaseAdmin/Auth/ISigner.cs
+++ b/FirebaseAdmin/FirebaseAdmin/Auth/ISigner.cs
@@ -22,7 +22,7 @@ namespace FirebaseAdmin.Auth
/// Represents an object can be used to cryptographically sign data. Mainly used for signing
/// custom JWT tokens issued to Firebase users.
///
- internal interface ISigner: IDisposable
+ internal interface ISigner : IDisposable
{
///
/// Returns the ID (client email) of the service account used to sign payloads.
diff --git a/FirebaseAdmin/FirebaseAdmin/Auth/JwtUtils.cs b/FirebaseAdmin/FirebaseAdmin/Auth/JwtUtils.cs
index 7fb5ab74..b252236f 100644
--- a/FirebaseAdmin/FirebaseAdmin/Auth/JwtUtils.cs
+++ b/FirebaseAdmin/FirebaseAdmin/Auth/JwtUtils.cs
@@ -25,12 +25,6 @@ namespace FirebaseAdmin.Auth
///
internal static class JwtUtils
{
- private static string Encode(object obj)
- {
- var json = NewtonsoftJsonSerializer.Instance.Serialize(obj);
- return UrlSafeBase64Encode(Encoding.UTF8.GetBytes(json));
- }
-
///
/// Decodes a single JWT segment, and deserializes it into a value of type
/// .
@@ -44,19 +38,13 @@ public static T Decode(string value)
return NewtonsoftJsonSerializer.Instance.Deserialize(json);
}
- private static string UrlSafeBase64Encode(byte[] bytes)
- {
- var base64Value = Convert.ToBase64String(bytes);
- return base64Value.TrimEnd('=').Replace('+', '-').Replace('/', '_');
- }
-
- public static string Base64Decode(string input)
+ internal static string Base64Decode(string input)
{
var raw = Base64DecodeToBytes(input);
return Encoding.UTF8.GetString(raw);
}
- public static byte[] Base64DecodeToBytes(string input)
+ internal static byte[] Base64DecodeToBytes(string input)
{
// undo the url safe replacements
input = input.Replace('-', '+').Replace('_', '/');
@@ -65,11 +53,14 @@ public static byte[] Base64DecodeToBytes(string input)
case 2: input += "=="; break;
case 3: input += "="; break;
}
+
return Convert.FromBase64String(input);
}
- public static async Task CreateSignedJwtAsync(
- object header, object payload, ISigner signer,
+ internal static async Task CreateSignedJwtAsync(
+ object header,
+ object payload,
+ ISigner signer,
CancellationToken cancellationToken = default(CancellationToken))
{
string encodedHeader = Encode(header);
@@ -85,5 +76,17 @@ public static async Task CreateSignedJwtAsync(
assertion.Append('.').Append(UrlSafeBase64Encode(signature));
return assertion.ToString();
}
+
+ private static string Encode(object obj)
+ {
+ var json = NewtonsoftJsonSerializer.Instance.Serialize(obj);
+ return UrlSafeBase64Encode(Encoding.UTF8.GetBytes(json));
+ }
+
+ private static string UrlSafeBase64Encode(byte[] bytes)
+ {
+ var base64Value = Convert.ToBase64String(bytes);
+ return base64Value.TrimEnd('=').Replace('+', '-').Replace('/', '_');
+ }
}
}
diff --git a/FirebaseAdmin/FirebaseAdmin/Auth/PublicKey.cs b/FirebaseAdmin/FirebaseAdmin/Auth/PublicKey.cs
index e1779633..483f5f7c 100644
--- a/FirebaseAdmin/FirebaseAdmin/Auth/PublicKey.cs
+++ b/FirebaseAdmin/FirebaseAdmin/Auth/PublicKey.cs
@@ -27,21 +27,21 @@ namespace FirebaseAdmin.Auth
///
internal sealed class PublicKey
{
+ public PublicKey(string keyId, RSAKey rsa)
+ {
+ this.Id = keyId;
+ this.RSA = rsa;
+ }
+
///
- /// The unique identifier of this key.
+ /// Gets the unique identifier of this key.
///
public string Id { get; }
///
- /// A instance containing the contents of
+ /// Gets the instance containing the contents of
/// the public key.
///
public RSAKey RSA { get; }
-
- public PublicKey(string keyId, RSAKey rsa)
- {
- Id = keyId;
- RSA = rsa;
- }
}
}
diff --git a/FirebaseAdmin/FirebaseAdmin/Auth/ServiceAccountSigner.cs b/FirebaseAdmin/FirebaseAdmin/Auth/ServiceAccountSigner.cs
index adfc064d..79b782e7 100644
--- a/FirebaseAdmin/FirebaseAdmin/Auth/ServiceAccountSigner.cs
+++ b/FirebaseAdmin/FirebaseAdmin/Auth/ServiceAccountSigner.cs
@@ -16,6 +16,7 @@
using System.Threading;
using System.Threading.Tasks;
using Google.Apis.Auth.OAuth2;
+using Google.Apis.Util;
namespace FirebaseAdmin.Auth
{
@@ -23,31 +24,27 @@ namespace FirebaseAdmin.Auth
/// An implementation that uses service account credentials to sign
/// data. Uses the private key present in the credential to produce signatures.
///
- internal sealed class ServiceAccountSigner: ISigner
+ internal sealed class ServiceAccountSigner : ISigner
{
- private readonly ServiceAccountCredential _credential;
+ private readonly ServiceAccountCredential credential;
public ServiceAccountSigner(ServiceAccountCredential credential)
{
- if (credential == null)
- {
- throw new ArgumentNullException("Credential must not be null.");
- }
- _credential = credential;
+ this.credential = credential.ThrowIfNull(nameof(credential));
}
public Task GetKeyIdAsync(CancellationToken cancellationToken = default(CancellationToken))
{
- return Task.FromResult(_credential.Id);
+ return Task.FromResult(this.credential.Id);
}
public Task SignDataAsync(byte[] data, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
- var signature = _credential.CreateSignature(data);
+ var signature = this.credential.CreateSignature(data);
return Task.FromResult(Convert.FromBase64String(signature));
}
- public void Dispose() {}
+ public void Dispose() { }
}
}
diff --git a/FirebaseAdmin/FirebaseAdmin/Auth/UserRecord.cs b/FirebaseAdmin/FirebaseAdmin/Auth/UserRecord.cs
index 69505630..ec584e87 100644
--- a/FirebaseAdmin/FirebaseAdmin/Auth/UserRecord.cs
+++ b/FirebaseAdmin/FirebaseAdmin/Auth/UserRecord.cs
@@ -12,11 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using Google.Apis.Json;
-using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
+using Google.Apis.Json;
+using Newtonsoft.Json;
namespace FirebaseAdmin.Auth
{
@@ -26,49 +26,49 @@ namespace FirebaseAdmin.Auth
///
internal class UserRecord
{
- private string _uid;
- private IReadOnlyDictionary _customClaims;
+ private string uid;
+ private IReadOnlyDictionary customClaims;
+
+ public UserRecord(string uid)
+ {
+ this.Uid = uid;
+ }
///
- /// The user ID of this user.
+ /// Gets the user ID of this user.
///
[JsonProperty("localId")]
public string Uid
{
- get => _uid;
+ get => this.uid;
private set
{
CheckUid(value);
- _uid = value;
+ this.uid = value;
}
}
///
- /// Returns custom claims set on this user.
+ /// Gets or sets the custom claims set on this user.
///
[JsonIgnore]
public IReadOnlyDictionary CustomClaims
{
- get => _customClaims;
+ get => this.customClaims;
set
{
CheckCustomClaims(value);
- _customClaims = value;
+ this.customClaims = value;
}
}
[JsonProperty("customAttributes")]
internal string CustomClaimsString => SerializeClaims(CustomClaims);
- public UserRecord(string uid)
- {
- Uid = uid;
- }
-
///
/// Checks if the given user ID is valid.
///
- /// The user ID. Must not be null or longer than
+ /// The user ID. Must not be null or longer than
/// 128 characters.
public static void CheckUid(string uid)
{
@@ -85,10 +85,10 @@ public static void CheckUid(string uid)
///
/// Checks if the given set of custom claims are valid.
///
- /// The custom claims. Claim names must
+ /// The custom claims. Claim names must
/// not be null or empty and must not be reserved and the serialized
/// claims have to be less than 1000 bytes.
- public static void CheckCustomClaims(IReadOnlyDictionary customClaims)
+ internal static void CheckCustomClaims(IReadOnlyDictionary customClaims)
{
if (customClaims == null)
{
@@ -101,6 +101,7 @@ public static void CheckCustomClaims(IReadOnlyDictionary customC
{
throw new ArgumentException("Claim names must not be null or empty");
}
+
if (FirebaseTokenFactory.ReservedClaims.Contains(key))
{
throw new ArgumentException($"Claim {key} is reserved and cannot be set");
diff --git a/FirebaseAdmin/FirebaseAdmin/Extensions.cs b/FirebaseAdmin/FirebaseAdmin/Extensions.cs
index db5a2a02..650db9df 100644
--- a/FirebaseAdmin/FirebaseAdmin/Extensions.cs
+++ b/FirebaseAdmin/FirebaseAdmin/Extensions.cs
@@ -33,21 +33,28 @@ internal static class Extensions
/// . Returns null if the GoogleCredential is not
/// based on a service account.
///
+ /// A service account credential if available, or null.
+ /// The Google credential from which to extract service account
+ /// credentials.
public static ServiceAccountCredential ToServiceAccountCredential(
this GoogleCredential credential)
{
if (credential.UnderlyingCredential is GoogleCredential)
{
- return ((GoogleCredential) credential.UnderlyingCredential)
+ return ((GoogleCredential)credential.UnderlyingCredential)
.ToServiceAccountCredential();
}
+
return credential.UnderlyingCredential as ServiceAccountCredential;
}
///
/// Creates a default (unauthenticated) from the
/// factory.
- ///
+ ///
+ /// An HTTP client that can be used to make unauthenticated requests.
+ /// The used to create
+ /// the HTTP client.
public static ConfigurableHttpClient CreateDefaultHttpClient(
this HttpClientFactory clientFactory)
{
@@ -58,6 +65,11 @@ public static ConfigurableHttpClient CreateDefaultHttpClient(
/// Creates an authenticated from the
/// factory.
///
+ /// An HTTP client that can be used to OAuth2 authorized requests.
+ /// The used to create
+ /// the HTTP client.
+ /// The Google credential that will be used to authenticate
+ /// outgoing HTTP requests.
public static ConfigurableHttpClient CreateAuthorizedHttpClient(
this HttpClientFactory clientFactory, GoogleCredential credential)
{
@@ -69,6 +81,14 @@ public static ConfigurableHttpClient CreateAuthorizedHttpClient(
///
/// Makes a JSON POST request using the given parameters.
///
+ /// An representing the response to the
+ /// POST request.
+ /// Type of the object that will be serialized into JSON.
+ /// The used to make the request.
+ /// URI for the outgoing request.
+ /// The object that will be serialized as the JSON body.
+ /// A cancellation token to monitor the asynchronous
+ /// operation.
public static async Task PostJsonAsync(
this HttpClient client, string requestUri, T body, CancellationToken cancellationToken)
{
@@ -81,9 +101,12 @@ public static async Task PostJsonAsync(
///
/// Returns a Unix-styled timestamp (seconds from epoch) from the .
///
+ /// Number of seconds since epoch.
+ /// The used to generate the timestamp.
public static long UnixTimestamp(this IClock clock)
{
- return (long) (clock.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
+ var timeSinceEpoch = clock.UtcNow.Subtract(new DateTime(1970, 1, 1));
+ return (long)timeSinceEpoch.TotalSeconds;
}
}
}
diff --git a/FirebaseAdmin/FirebaseAdmin/FirebaseAdmin.csproj b/FirebaseAdmin/FirebaseAdmin/FirebaseAdmin.csproj
index e5a8a453..7ecd116f 100644
--- a/FirebaseAdmin/FirebaseAdmin/FirebaseAdmin.csproj
+++ b/FirebaseAdmin/FirebaseAdmin/FirebaseAdmin.csproj
@@ -21,11 +21,16 @@
https://github.com/Firebase/firebase-admin-dotnet
git
https://github.com/Firebase/firebase-admin-dotnet
+ ../../stylecop.ruleset
+
+ all
+
+
diff --git a/FirebaseAdmin/FirebaseAdmin/FirebaseApp.cs b/FirebaseAdmin/FirebaseAdmin/FirebaseApp.cs
index 0c06aed3..9f12ecd2 100644
--- a/FirebaseAdmin/FirebaseAdmin/FirebaseApp.cs
+++ b/FirebaseAdmin/FirebaseAdmin/FirebaseApp.cs
@@ -18,18 +18,19 @@
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Google;
-using Google.Apis.Logging;
using Google.Apis.Auth.OAuth2;
+using Google.Apis.Logging;
-[assembly: InternalsVisibleToAttribute("FirebaseAdmin.Tests,PublicKey="+
-"002400000480000094000000060200000024000052534131000400000100010081328559eaab41"+
-"055b84af73469863499d81625dcbba8d8decb298b69e0f783a0958cf471fd4f76327b85a7d4b02"+
-"3003684e85e61cf15f13150008c81f0b75a252673028e530ea95d0c581378da8c6846526ab9597"+
-"4c6d0bc66d2462b51af69968a0e25114bde8811e0d6ee1dc22d4a59eee6a8bba4712cba839652f"+
+[assembly: InternalsVisibleToAttribute("FirebaseAdmin.Tests,PublicKey=" +
+"002400000480000094000000060200000024000052534131000400000100010081328559eaab41" +
+"055b84af73469863499d81625dcbba8d8decb298b69e0f783a0958cf471fd4f76327b85a7d4b02" +
+"3003684e85e61cf15f13150008c81f0b75a252673028e530ea95d0c581378da8c6846526ab9597" +
+"4c6d0bc66d2462b51af69968a0e25114bde8811e0d6ee1dc22d4a59eee6a8bba4712cba839652f" +
"badddb9c")]
-namespace FirebaseAdmin
+namespace FirebaseAdmin
{
- internal delegate TResult ServiceFactory() where TResult: IFirebaseService;
+ internal delegate TResult ServiceFactory()
+ where TResult : IFirebaseService;
///
/// This is the entry point to the Firebase Admin SDK. It holds configuration and state common
@@ -40,138 +41,97 @@ namespace FirebaseAdmin
///
public sealed class FirebaseApp
{
- private const string DefaultAppName = "[DEFAULT]";
-
internal static readonly IReadOnlyList DefaultScopes = ImmutableList.Create(
- // Enables access to Firebase Realtime Database.
- "https://www.googleapis.com/auth/firebase",
-
- // Enables access to the email address associated with a project.
- "https://www.googleapis.com/auth/userinfo.email",
-
- // Enables access to Google Identity Toolkit (for user management APIs).
- "https://www.googleapis.com/auth/identitytoolkit",
+ "https://www.googleapis.com/auth/firebase", // RTDB.
+ "https://www.googleapis.com/auth/userinfo.email", // RTDB
+ "https://www.googleapis.com/auth/identitytoolkit", // User management
+ "https://www.googleapis.com/auth/devstorage.full_control", // Cloud Storage
+ "https://www.googleapis.com/auth/cloud-platform", // Cloud Firestore
+ "https://www.googleapis.com/auth/datastore");
- // Enables access to Google Cloud Storage.
- "https://www.googleapis.com/auth/devstorage.full_control",
-
- // Enables access to Google Cloud Firestore
- "https://www.googleapis.com/auth/cloud-platform",
- "https://www.googleapis.com/auth/datastore"
- );
+ private const string DefaultAppName = "[DEFAULT]";
private static readonly Dictionary Apps = new Dictionary();
private static readonly ILogger Logger = ApplicationContext.Logger.ForType();
// Guards the mutable state local to an app instance.
- private readonly Object _lock = new Object();
- private bool _deleted = false;
- private readonly AppOptions _options;
-
- ///
- /// A copy of the this app was created with.
- ///
- public AppOptions Options
- {
- get
- {
- return new AppOptions(_options);
- }
- }
-
- ///
- /// Name of this app.
- ///
- public string Name { get; }
+ private readonly object appLock = new object();
+ private readonly AppOptions options;
// A collection of stateful services initialized using this app instance (e.g.
// FirebaseAuth). Services are tracked here so they can be cleaned up when the app is
// deleted.
- private readonly Dictionary _services = new Dictionary();
+ private readonly Dictionary services = new Dictionary();
+ private bool deleted = false;
private FirebaseApp(AppOptions options, string name)
{
- _options = new AppOptions(options);
- if (_options.Credential == null)
+ this.options = new AppOptions(options);
+ if (this.options.Credential == null)
{
throw new ArgumentNullException("Credential must be set");
}
- if (_options.Credential.IsCreateScopedRequired)
+
+ if (this.options.Credential.IsCreateScopedRequired)
{
- _options.Credential = _options.Credential.CreateScoped(DefaultScopes);
+ this.options.Credential = this.options.Credential.CreateScoped(DefaultScopes);
}
- Name = name;
+
+ this.Name = name;
}
///
- /// Deletes this app instance and cleans up any state associated with it. Once an app has
- /// been deleted, accessing any services related to it will result in an exception.
- /// If the app is already deleted, this method is a no-op.
+ /// Gets the default app instance. This property is null if the default app instance
+ /// doesn't yet exist.
///
- public void Delete()
+ public static FirebaseApp DefaultInstance
{
- // Clean up local state
- lock (_lock)
- {
- _deleted = true;
- foreach (var entry in _services)
- {
- try
- {
- entry.Value.Delete();
- }
- catch (Exception e)
- {
- Logger.Error(e, "Error while cleaning up service {0}", entry.Key);
- }
- }
- _services.Clear();
- }
- // Clean up global state
- lock (Apps)
+ get
{
- Apps.Remove(Name);
+ return GetInstance(DefaultAppName);
}
}
- internal T GetOrInit(string id, ServiceFactory initializer) where T : class, IFirebaseService
+ ///
+ /// Gets a copy of the this app was created with.
+ ///
+ public AppOptions Options
{
- lock (_lock)
+ get
{
- if (_deleted)
- {
- throw new InvalidOperationException("Cannot use an app after it has been deleted");
- }
- IFirebaseService service;
- if (!_services.TryGetValue(id, out service))
- {
- service = initializer();
- _services.Add(id, service);
- }
- return (T) service;
+ return new AppOptions(this.options);
}
}
- internal string GetProjectId()
+ ///
+ /// Gets the name of this app.
+ ///
+ public string Name { get; }
+
+ ///
+ /// Returns the app instance identified by the given name.
+ ///
+ /// The instance with the specified name or null if it
+ /// doesn't exist.
+ /// If the name argument is null or empty.
+ /// Name of the app to retrieve.
+ public static FirebaseApp GetInstance(string name)
{
- if (!string.IsNullOrEmpty(Options.ProjectId))
- {
- return Options.ProjectId;
- }
- var projectId = Options.Credential.ToServiceAccountCredential()?.ProjectId;
- if (!String.IsNullOrEmpty(projectId))
+ if (string.IsNullOrEmpty(name))
{
- return projectId;
+ throw new ArgumentException("App name to lookup must not be null or empty");
}
- foreach (var variableName in new [] {"GOOGLE_CLOUD_PROJECT", "GCLOUD_PROJECT"})
+
+ lock (Apps)
{
- projectId = Environment.GetEnvironmentVariable(variableName);
- if (!String.IsNullOrEmpty(projectId))
+ FirebaseApp app;
+ if (Apps.TryGetValue(name, out app))
{
- return projectId;
+ return app;
}
}
+
return null;
}
@@ -226,6 +186,7 @@ public static FirebaseApp Create(AppOptions options, string name)
{
throw new ArgumentException("App name must not be null or empty");
}
+
options = options ?? GetOptionsFromEnvironment();
lock (Apps)
{
@@ -235,59 +196,49 @@ public static FirebaseApp Create(AppOptions options, string name)
{
throw new ArgumentException("The default FirebaseApp already exists.");
}
- else
+ else
{
throw new ArgumentException($"FirebaseApp named {name} already exists.");
}
}
+
var app = new FirebaseApp(options, name);
Apps.Add(name, app);
return app;
}
}
- private static AppOptions GetOptionsFromEnvironment()
- {
- return new AppOptions()
- {
- Credential = GoogleCredential.GetApplicationDefault(),
- };
- }
-
///
- /// The default app instance. This property is null if the default app instance
- /// doesn't yet exist.
+ /// Deletes this app instance and cleans up any state associated with it. Once an app has
+ /// been deleted, accessing any services related to it will result in an exception.
+ /// If the app is already deleted, this method is a no-op.
///
- public static FirebaseApp DefaultInstance
+ public void Delete()
{
- get
+ // Clean up local state
+ lock (this.appLock)
{
- return GetInstance(DefaultAppName);
- }
- }
+ this.deleted = true;
+ foreach (var entry in this.services)
+ {
+ try
+ {
+ entry.Value.Delete();
+ }
+ catch (Exception e)
+ {
+ Logger.Error(e, "Error while cleaning up service {0}", entry.Key);
+ }
+ }
- ///
- /// Returns the app instance identified by the given name.
- ///
- /// The instance with the specified name or null if it
- /// doesn't exist.
- /// If the name argument is null or empty.
- /// Name of the app to retrieve.
- public static FirebaseApp GetInstance(string name)
- {
- if (string.IsNullOrEmpty(name))
- {
- throw new ArgumentException("App name to lookup must not be null or empty");
+ this.services.Clear();
}
- lock (Apps)
+
+ // Clean up global state
+ lock (Apps)
{
- FirebaseApp app;
- if (Apps.TryGetValue(name, out app))
- {
- return app;
- }
+ Apps.Remove(this.Name);
}
- return null;
}
///
@@ -302,11 +253,74 @@ internal static void DeleteAll()
{
entry.Value.Delete();
}
+
if (Apps.Count > 0)
{
throw new InvalidOperationException("Failed to delete all apps");
}
- }
+ }
+ }
+
+ internal T GetOrInit(string id, ServiceFactory initializer)
+ where T : class, IFirebaseService
+ {
+ lock (this.appLock)
+ {
+ if (this.deleted)
+ {
+ throw new InvalidOperationException("Cannot use an app after it has been deleted");
+ }
+
+ IFirebaseService service;
+ if (!this.services.TryGetValue(id, out service))
+ {
+ service = initializer();
+ this.services.Add(id, service);
+ }
+
+ return (T)service;
+ }
+ }
+
+ ///
+ /// Returns the Google Cloud Platform project ID associated with this Firebase app. If a
+ /// project ID is specified in , that value is returned. If not
+ /// attempts to determine a project ID from the used to
+ /// initialize the app. Looks up the GOOGLE_CLOUD_PROJECT environment variable when all
+ /// else fails.
+ ///
+ /// A project ID string or null.
+ internal string GetProjectId()
+ {
+ if (!string.IsNullOrEmpty(this.Options.ProjectId))
+ {
+ return this.Options.ProjectId;
+ }
+
+ var projectId = this.Options.Credential.ToServiceAccountCredential()?.ProjectId;
+ if (!string.IsNullOrEmpty(projectId))
+ {
+ return projectId;
+ }
+
+ foreach (var variableName in new[] { "GOOGLE_CLOUD_PROJECT", "GCLOUD_PROJECT" })
+ {
+ projectId = Environment.GetEnvironmentVariable(variableName);
+ if (!string.IsNullOrEmpty(projectId))
+ {
+ return projectId;
+ }
+ }
+
+ return null;
+ }
+
+ private static AppOptions GetOptionsFromEnvironment()
+ {
+ return new AppOptions()
+ {
+ Credential = GoogleCredential.GetApplicationDefault(),
+ };
}
}
}
diff --git a/FirebaseAdmin/FirebaseAdmin/FirebaseException.cs b/FirebaseAdmin/FirebaseAdmin/FirebaseException.cs
index a2032d0a..e2a52733 100644
--- a/FirebaseAdmin/FirebaseAdmin/FirebaseException.cs
+++ b/FirebaseAdmin/FirebaseAdmin/FirebaseException.cs
@@ -19,10 +19,12 @@ namespace FirebaseAdmin
///
/// Common error type for all exceptions raised by Firebase APIs.
///
- public sealed class FirebaseException: Exception
+ public sealed class FirebaseException : Exception
{
- internal FirebaseException(string message): base(message) {}
-
- internal FirebaseException(string message, Exception inner): base(message, inner) {}
+ internal FirebaseException(string message)
+ : base(message) { }
+
+ internal FirebaseException(string message, Exception inner)
+ : base(message, inner) { }
}
}
diff --git a/FirebaseAdmin/FirebaseAdmin/IFirebaseService.cs b/FirebaseAdmin/FirebaseAdmin/IFirebaseService.cs
index d6075f4a..3407ef9f 100644
--- a/FirebaseAdmin/FirebaseAdmin/IFirebaseService.cs
+++ b/FirebaseAdmin/FirebaseAdmin/IFirebaseService.cs
@@ -20,6 +20,9 @@ namespace FirebaseAdmin
///
internal interface IFirebaseService
{
+ ///
+ /// Cleans up any state associated with this service making it unsuitable for further use.
+ ///
void Delete();
}
}
diff --git a/stylecop.json b/stylecop.json
new file mode 100644
index 00000000..56f05687
--- /dev/null
+++ b/stylecop.json
@@ -0,0 +1,8 @@
+{
+ "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
+ "settings": {
+ "documentationRules": {
+ "documentInternalElements": false
+ }
+ }
+}
diff --git a/stylecop.ruleset b/stylecop.ruleset
new file mode 100644
index 00000000..8a4c10a4
--- /dev/null
+++ b/stylecop.ruleset
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/stylecop_test.ruleset b/stylecop_test.ruleset
new file mode 100644
index 00000000..aaef9717
--- /dev/null
+++ b/stylecop_test.ruleset
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+