-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
48c352c
commit c704cec
Showing
45 changed files
with
3,151 additions
and
87 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
using System.Security.Cryptography; | ||
using System.Text; | ||
|
||
namespace SoundShapesServer.Common; | ||
|
||
public static class HashHelper | ||
{ | ||
public static string ComputeSha512Hash(string input) | ||
{ | ||
using SHA512 sha512 = SHA512.Create(); | ||
|
||
// Convert the input string to a byte array | ||
byte[] inputBytes = Encoding.UTF8.GetBytes(input); | ||
|
||
// Compute the hash | ||
byte[] hashBytes = sha512.ComputeHash(inputBytes); | ||
|
||
// Convert the byte array to a hexadecimal string | ||
StringBuilder sb = new StringBuilder(); | ||
foreach (byte b in hashBytes) | ||
{ | ||
sb.Append(b.ToString("x2")); | ||
} | ||
|
||
return sb.ToString(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
SoundShapesServer.Common/Verification/SoundShapesSigningKey.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
using NPTicket.Verification.Keys; | ||
|
||
namespace SoundShapesServer.Common.Verification; | ||
|
||
public class SoundShapesSigningKey : PsnSigningKey | ||
{ | ||
public static readonly SoundShapesSigningKey Instance = new(); | ||
private SoundShapesSigningKey() {} | ||
|
||
public override string CurveX => "39c62d061d4ee35c5f3f7531de0af3cf918346526edac727"; | ||
public override string CurveY => "a5d578b55113e612bf1878d4cc939d61a41318403b5bdf86"; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
95 changes: 95 additions & 0 deletions
95
SoundShapesServer.Tests/Tests/Authentication/TokenTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
using System.Net.Http.Json; | ||
using SoundShapesServer.Common.Constants; | ||
using SoundShapesServer.Tests.Server; | ||
using SoundShapesServer.Types; | ||
using SoundShapesServer.Types.Database; | ||
using SoundShapesServer.Types.Requests.Api; | ||
using SoundShapesServer.Types.Responses.Api.ApiTypes; | ||
using SoundShapesServer.Types.Responses.Api.DataTypes; | ||
|
||
namespace SoundShapesServer.Tests.Tests.Authentication; | ||
|
||
public class TokenTests : ServerTest | ||
{ | ||
[Test] | ||
public void RevokeTokenWorks() | ||
{ | ||
using SSTestContext context = this.GetServer(); | ||
|
||
DbUser user = context.CreateUser(); | ||
DbRefreshToken refreshToken = context.Database.CreateRefreshToken(user); | ||
|
||
List<DbToken> tokens = new(); | ||
for (int i = 0; i < 5; i++) | ||
{ | ||
tokens.Add(context.Database.CreateApiTokenWithRefreshToken(refreshToken)); | ||
} | ||
|
||
HttpClient client = context.GetAuthenticatedClient(tokens.First()); | ||
|
||
HttpResponseMessage response = client.PostAsync("/api/v1/revokeToken", null).Result; | ||
Assert.That(response.IsSuccessStatusCode); | ||
foreach (DbToken token in tokens) | ||
{ | ||
Assert.That(context.Database.GetTokenWithId(token.Id), Is.EqualTo(null)); | ||
} | ||
Assert.That(context.Database.GetRefreshTokenWithId(refreshToken.Id), Is.EqualTo(null)); | ||
} | ||
|
||
[Test] | ||
public void TokenExpiryWorks() | ||
{ | ||
using SSTestContext context = this.GetServer(); | ||
|
||
HttpClient client = context.GetAuthenticatedClient(TokenType.ApiAccess); | ||
|
||
HttpResponseMessage response = client.GetAsync("/api/v1/users/me").Result; | ||
Assert.That(response.IsSuccessStatusCode); | ||
|
||
context.Time.Now = context.Time.Now.AddYears(1); | ||
|
||
response = client.GetAsync("/api/v1/users/me").Result; | ||
Assert.That(!response.IsSuccessStatusCode); | ||
} | ||
|
||
[Test] | ||
public void RefreshTokenExpiryWorks() | ||
{ | ||
using SSTestContext context = this.GetServer(); | ||
using HttpClient client = context.Http; | ||
|
||
DbUser user = context.CreateUser(); | ||
DbRefreshToken refreshToken = context.Database.CreateRefreshToken(user); | ||
|
||
// get right before refresh token expires | ||
context.Time.Now = context.Time.Now.AddHours(ExpiryTimes.RefreshTokenHours).AddMinutes(-1); | ||
HttpResponseMessage response = client.PostAsJsonAsync("/api/v1/refreshToken", new ApiRefreshTokenRequest | ||
{ | ||
RefreshTokenId = refreshToken.Id | ||
}).Result; | ||
Assert.That(response.IsSuccessStatusCode); | ||
|
||
// assure that the generated access token works | ||
ApiLoginResponse? deSerialized = | ||
response.Content.ReadFromJsonAsync<ApiResponse<ApiLoginResponse>>().Result!.Data; | ||
Assert.That(deSerialized != null); | ||
DbToken? accessToken = context.Database.GetTokenWithId(deSerialized!.AccessToken.Id); | ||
Assert.That(accessToken != null); | ||
|
||
// get right after refresh token expires | ||
context.Time.Now = context.Time.Now.AddMinutes(2); | ||
|
||
response = client.PostAsJsonAsync("/api/v1/refreshToken", new ApiRefreshTokenRequest | ||
{ | ||
RefreshTokenId = refreshToken.Id | ||
}).Result; | ||
|
||
// check that the refresh token no longer works after the expiry date | ||
Assert.That(!response.IsSuccessStatusCode); | ||
|
||
// check that the access token was generated with the refresh token has been revoked | ||
using HttpClient authClient = context.GetAuthenticatedClient(accessToken!); | ||
response = client.GetAsync("/api/v1/users/me").Result; | ||
Assert.That(!response.IsSuccessStatusCode); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
using Microsoft.EntityFrameworkCore; | ||
using Microsoft.EntityFrameworkCore.ChangeTracking; | ||
using SoundShapesServer.Types.Database; | ||
|
||
namespace SoundShapesServer.Database; | ||
|
||
public partial class GameDatabaseContext | ||
{ | ||
public DbIp GetOrCreateIp(DbUser user, string ipAddress) | ||
{ | ||
DbIp? existingIp = Ips.Include(i => i.User) | ||
.FirstOrDefault(i => i.UserId == user.Id && i.IpAddress == ipAddress); | ||
|
||
if (existingIp != null) | ||
return existingIp; | ||
|
||
EntityEntry<DbIp> ip = Ips.Add(new DbIp | ||
{ | ||
IpAddress = ipAddress, | ||
CreationDate = Time.Now, | ||
UserId = user.Id | ||
}); | ||
|
||
SaveChanges(); | ||
|
||
return ip.Entity; | ||
} | ||
} |
54 changes: 54 additions & 0 deletions
54
SoundShapesServer/Database/GameDatabaseContext.RefreshTokens.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
using Microsoft.EntityFrameworkCore; | ||
using Microsoft.EntityFrameworkCore.ChangeTracking; | ||
using SoundShapesServer.Common.Constants; | ||
using SoundShapesServer.Types; | ||
using SoundShapesServer.Types.Database; | ||
|
||
namespace SoundShapesServer.Database; | ||
|
||
public partial class GameDatabaseContext | ||
{ | ||
public DbToken CreateApiTokenWithRefreshToken(DbRefreshToken refresh) | ||
{ | ||
return CreateToken(refresh.User, TokenType.ApiAccess, null, null, refresh, null); | ||
} | ||
|
||
public DbRefreshToken? GetRefreshTokenWithId(Guid guid) | ||
{ | ||
DbRefreshToken? token = RefreshTokens | ||
.Include(t => t.User) | ||
.FirstOrDefault(t => t.Id == guid); | ||
|
||
if (_time.Now >= token?.ExpiryDate) | ||
{ | ||
RemoveRefreshToken(token); | ||
return null; | ||
} | ||
|
||
return token; | ||
} | ||
|
||
private void RemoveRefreshToken(DbRefreshToken token) | ||
{ | ||
RefreshTokens.Remove(token); | ||
|
||
SaveChanges(); | ||
} | ||
|
||
public DbRefreshToken CreateRefreshToken(DbUser user) | ||
{ | ||
EntityEntry<DbRefreshToken> token = RefreshTokens.Add(new DbRefreshToken | ||
{ | ||
UserId = user.Id, | ||
CreationDate = _time.Now, | ||
ExpiryDate = _time.Now.AddHours(ExpiryTimes.RefreshTokenHours) | ||
}); | ||
|
||
SaveChanges(); | ||
|
||
// Reload to load the ID | ||
token.Reload(); | ||
|
||
return token.Entity; | ||
} | ||
} |
Oops, something went wrong.