✅ Override JWT Bearer service for Tests
My app uses this setup for its authentication:
I then usually extract my userId's like this:
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:
For "Test" JWT's and use a helper function to create these during my tests?
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
};
}
);
public string UserId()
{
return _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
}
public string UserId()
{
return _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
}
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
};
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
I've got some examples lying about that I'll need to look up
I'm out though
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());
});
});
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 ...
In my test class I have something like ...
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
.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));
}
}
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)
});
}
Use the /close command to mark a forum thread as answered