Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 27 additions & 13 deletions src/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -218,25 +218,39 @@ private string BuildUrl(string path, Dictionary<string, string>? queryParams, Di
return url;
}

private string GenerateServerSideToken()
/// <summary>
/// Creates a JWT token for the given user ID.
/// </summary>
/// <param name="userId">The user ID to create the token for.</param>
/// <param name="expiration">Optional token lifetime. Defaults to 1 hour.</param>
/// <returns>A signed JWT token string.</returns>
public string CreateUserToken(string userId, TimeSpan? expiration = null)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(ApiSecret);
var claims = new List<System.Security.Claims.Claim>
if (string.IsNullOrEmpty(userId))
throw new ArgumentException("User ID cannot be null or empty.", nameof(userId));

return CreateJwtToken(new SecurityTokenDescriptor
{
// new System.Security.Claims.Claim("user_id", "*"),
// new System.Security.Claims.Claim("user", "*")
};
Claims = new Dictionary<string, object> { { "user_id", userId } },
Expires = DateTime.UtcNow.Add(expiration ?? TimeSpan.FromHours(1)),
});
}

var tokenDescriptor = new SecurityTokenDescriptor
private string GenerateServerSideToken()
{
return CreateJwtToken(new SecurityTokenDescriptor
{
Subject = new System.Security.Claims.ClaimsIdentity(claims),
Subject = new System.Security.Claims.ClaimsIdentity(),
Expires = DateTime.UtcNow.AddHours(1),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
});
}

var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
private string CreateJwtToken(SecurityTokenDescriptor descriptor)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.UTF8.GetBytes(ApiSecret);
descriptor.SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature);
return tokenHandler.WriteToken(tokenHandler.CreateToken(descriptor));
}

private MultipartFormDataContent CreateMultipartContent(FileUploadRequest request)
Expand Down
91 changes: 91 additions & 0 deletions tests/CreateUserTokenTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Text;
using GetStream;
using Microsoft.IdentityModel.Tokens;
using NUnit.Framework;

namespace GetStream.Tests
{
[TestFixture]
public class CreateUserTokenTests
{
private const string TestApiKey = "test-api-key";
private const string TestApiSecret = "test-api-secret-that-is-long-enough-for-hmac256";

private StreamClient _client = null!;
private JwtSecurityTokenHandler _tokenHandler = null!;

[SetUp]
public void SetUp()
{
_client = new StreamClient(TestApiKey, TestApiSecret);
_tokenHandler = new JwtSecurityTokenHandler();
}

[Test]
public void CreateUserToken_ContainsCorrectUserId()
{
var token = _client.CreateUserToken("john");
var jwt = _tokenHandler.ReadJwtToken(token);

Assert.That(jwt.Payload["user_id"], Is.EqualTo("john"));
}

[Test]
public void CreateUserToken_DefaultExpiration_IsOneHour()
{
var before = DateTime.UtcNow;
var token = _client.CreateUserToken("john");
var jwt = _tokenHandler.ReadJwtToken(token);

var exp = jwt.ValidTo;
Assert.That(exp, Is.GreaterThan(before.AddMinutes(59)));
Assert.That(exp, Is.LessThan(before.AddMinutes(61)));
}

[Test]
public void CreateUserToken_CustomExpiration()
{
var before = DateTime.UtcNow;
var token = _client.CreateUserToken("john", expiration: TimeSpan.FromHours(24));
var jwt = _tokenHandler.ReadJwtToken(token);

var exp = jwt.ValidTo;
Assert.That(exp, Is.GreaterThan(before.AddHours(23)));
Assert.That(exp, Is.LessThan(before.AddHours(25)));
}

[Test]
public void CreateUserToken_IsValidSignature()
{
var token = _client.CreateUserToken("john");
var key = Encoding.UTF8.GetBytes(TestApiSecret);

var validationParams = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = false,
IssuerSigningKey = new SymmetricSecurityKey(key),
};

_tokenHandler.ValidateToken(token, validationParams, out _);
}

[Test]
public void CreateUserToken_DifferentUsers_ProduceDifferentTokens()
{
var token1 = _client.CreateUserToken("alice");
var token2 = _client.CreateUserToken("bob");

Assert.That(token1, Is.Not.EqualTo(token2));

var jwt1 = _tokenHandler.ReadJwtToken(token1);
var jwt2 = _tokenHandler.ReadJwtToken(token2);

Assert.That(jwt1.Payload["user_id"], Is.EqualTo("alice"));
Assert.That(jwt2.Payload["user_id"], Is.EqualTo("bob"));
}
}
}
Loading