C
C#12mo ago
Nickolaki

✅ Override JWT Bearer service for Tests

My app uses this setup for its authentication:
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
...

options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = "https://darling-bug-65.clerk.accounts.dev",
ValidateIssuer = true,
ValidateLifetime = true,
ValidateAudience = false,
ValidateIssuerSigningKey = true,
ClockSkew = TimeSpan.Zero,
IssuerSigningKey = issuerSigningKey
};
}
);
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
...

options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = "https://darling-bug-65.clerk.accounts.dev",
ValidateIssuer = true,
ValidateLifetime = true,
ValidateAudience = false,
ValidateIssuerSigningKey = true,
ClockSkew = TimeSpan.Zero,
IssuerSigningKey = issuerSigningKey
};
}
);
I then usually extract my userId's like this:
public string UserId()
{
return _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
}
public string UserId()
{
return _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
}
What would be a good way to override this authentication so that I don't have to use real Clerk https://clerk.com/ JWT's during testing. I'm thinking that I simply override these params:
options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = "https://darling-bug-65.clerk.accounts.dev",
ValidateIssuer = true,
ValidateLifetime = true,
ValidateAudience = false,
ValidateIssuerSigningKey = true,
ClockSkew = TimeSpan.Zero,
IssuerSigningKey = issuerSigningKey
};
options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = "https://darling-bug-65.clerk.accounts.dev",
ValidateIssuer = true,
ValidateLifetime = true,
ValidateAudience = false,
ValidateIssuerSigningKey = true,
ClockSkew = TimeSpan.Zero,
IssuerSigningKey = issuerSigningKey
};
For "Test" JWT's and use a helper function to create these during my tests?
Clerk
Clerk | Authentication and User Management
The easiest way to add authentication and user management to your application. Purpose-built for React, Next.js, Remix, and “The Modern Web”.
4 Replies
Mayor McCheese
Mayor McCheese12mo ago
I've got some examples lying about that I'll need to look up I'm out though
Nickolaki
Nickolaki12mo ago
No worries bro, would really appreciate it! Btw I managed to get it sorted with something like this:
builder.ConfigureTestServices(services =>
{
services.RemoveJwtAuthentication();

services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
SecurityKey issuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("9f7b309b-1dcc-4a96-a292-dbe6e830d8c3"));

options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = "TestIssuer",
ValidAudience = "TestAudience",
ValidateIssuer = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ClockSkew = TimeSpan.Zero,
IssuerSigningKey = issuerSigningKey
};
}
);

services.RemoveDbContext<JobbyDbContext>();

services.AddDbContext<JobbyDbContext>(options =>
{
options.UseSqlServer(_mssqlContainer.GetConnectionString());
});
});
builder.ConfigureTestServices(services =>
{
services.RemoveJwtAuthentication();

services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
SecurityKey issuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("9f7b309b-1dcc-4a96-a292-dbe6e830d8c3"));

options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = "TestIssuer",
ValidAudience = "TestAudience",
ValidateIssuer = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ClockSkew = TimeSpan.Zero,
IssuerSigningKey = issuerSigningKey
};
}
);

services.RemoveDbContext<JobbyDbContext>();

services.AddDbContext<JobbyDbContext>(options =>
{
options.UseSqlServer(_mssqlContainer.GetConnectionString());
});
});
Mayor McCheese
Mayor McCheese12mo ago
I did something similarish, but I'll check in a bit; just got in the door In my web application factory I have something like ...
.ConfigureTestServices(services =>
{
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters.NameClaimType = "sub";
options.MetadataAddress =
"https://login.microsoftonline.com/common/.well-known/openid-configuration";
options.Audience = MockJwtTokens.Audience;

});

services.Configure<JwtBearerOptions>(
JwtBearerDefaults.AuthenticationScheme, options =>
{
var config = new OpenIdConnectConfiguration() { Issuer = MockJwtTokens.Issuer };

config.SigningKeys.Add(MockJwtTokens.SecurityKey);
options.Configuration = config;
});
});
.ConfigureTestServices(services =>
{
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters.NameClaimType = "sub";
options.MetadataAddress =
"https://login.microsoftonline.com/common/.well-known/openid-configuration";
options.Audience = MockJwtTokens.Audience;

});

services.Configure<JwtBearerOptions>(
JwtBearerDefaults.AuthenticationScheme, options =>
{
var config = new OpenIdConnectConfiguration() { Issuer = MockJwtTokens.Issuer };

config.SigningKeys.Add(MockJwtTokens.SecurityKey);
options.Configuration = config;
});
});
public static class MockJwtTokens
{
public static string Issuer { get; } = Guid.NewGuid().ToString();
public static string Audience = "https://integrationtest.locahost";
public static SecurityKey SecurityKey { get; }
public static SigningCredentials SigningCredentials { get; }

private static readonly JwtSecurityTokenHandler SecurityTokenHandler = new JwtSecurityTokenHandler();
private static readonly RandomNumberGenerator Random = RandomNumberGenerator.Create();
private static readonly byte[] Key = new byte[32];

static MockJwtTokens()
{
Random.GetBytes(Key);
SecurityKey = new SymmetricSecurityKey(Key) { KeyId = Guid.NewGuid().ToString() };
SigningCredentials = new SigningCredentials(SecurityKey, SecurityAlgorithms.HmacSha256);
}

public static string GenerateJwtToken(IEnumerable<Claim> claims)
{
return SecurityTokenHandler.WriteToken(new JwtSecurityToken(Issuer, Audience, claims, null, DateTime.UtcNow.AddMinutes(20), SigningCredentials));
}
}
public static class MockJwtTokens
{
public static string Issuer { get; } = Guid.NewGuid().ToString();
public static string Audience = "https://integrationtest.locahost";
public static SecurityKey SecurityKey { get; }
public static SigningCredentials SigningCredentials { get; }

private static readonly JwtSecurityTokenHandler SecurityTokenHandler = new JwtSecurityTokenHandler();
private static readonly RandomNumberGenerator Random = RandomNumberGenerator.Create();
private static readonly byte[] Key = new byte[32];

static MockJwtTokens()
{
Random.GetBytes(Key);
SecurityKey = new SymmetricSecurityKey(Key) { KeyId = Guid.NewGuid().ToString() };
SigningCredentials = new SigningCredentials(SecurityKey, SecurityAlgorithms.HmacSha256);
}

public static string GenerateJwtToken(IEnumerable<Claim> claims)
{
return SecurityTokenHandler.WriteToken(new JwtSecurityToken(Issuer, Audience, claims, null, DateTime.UtcNow.AddMinutes(20), SigningCredentials));
}
}
In my test class I have something like ...
protected string GenerateUserBearerToken(string scope, Guid? userId = null)
{
return MockJwtTokens.GenerateJwtToken(new Claim[]
{
new("sub", (userId ?? Guid.NewGuid()).ToString()),
new("scp", scope)
});
}
protected string GenerateUserBearerToken(string scope, Guid? userId = null)
{
return MockJwtTokens.GenerateJwtToken(new Claim[]
{
new("sub", (userId ?? Guid.NewGuid()).ToString()),
new("scp", scope)
});
}
that's part of a test base class that creates a bearer token which gets added the request as a bearer token @nickolaki if you're satisfied with your answer you can $close
MODiX
MODiX12mo ago
Use the /close command to mark a forum thread as answered
Want results from more Discord servers?
Add your server
More Posts