Skip to content

Commit

Permalink
#11 moved JWT config values into secrets/env
Browse files Browse the repository at this point in the history
  • Loading branch information
mgroves committed Aug 8, 2023
1 parent 57dfaff commit 5320b83
Show file tree
Hide file tree
Showing 19 changed files with 100 additions and 37 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/ci-capella.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ jobs:
Couchbase__ScopeName: ${{ secrets.COUCHBASE__SCOPENAME }}
Couchbase__UsersCollectionName: ${{ secrets.COUCHBASE__USERSCOLLECTIONNAME }}
Couchbase__FollowsCollectionName: ${{ secrets.COUCHBASE__FOLLOWSCOLLECTIONNAME }}
JwtSecret__Issuer: ${{ secrets.JWTSECRET__ISSUER }}
JwtSecret__Audience: ${{ secrets.JWTSECRET__AUDIENCE }}
JwtSecret__SecurityKey: ${{ secrets.JWTSECRET__SECURITYKEY }}

steps:
- name: Checkout
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/ci-container.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ jobs:
Couchbase__ScopeName: ${{ secrets.COUCHBASE__SCOPENAME }}
Couchbase__UsersCollectionName: ${{ secrets.COUCHBASE__USERSCOLLECTIONNAME }}
Couchbase__FollowsCollectionName: ${{ secrets.COUCHBASE__FOLLOWSCOLLECTIONNAME }}
JwtSecret__Issuer: ${{ secrets.JWTSECRET__ISSUER }}
JwtSecret__Audience: ${{ secrets.JWTSECRET__AUDIENCE }}
JwtSecret__SecurityKey: ${{ secrets.JWTSECRET__SECURITYKEY }}

services:
couchbase:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Conduit.Tests.TestHelpers.Data;
using Conduit.Tests.TestHelpers;
using Conduit.Tests.TestHelpers.Data;
using Conduit.Web.Follows.Handlers;
using Conduit.Web.Follows.Services;
using Conduit.Web.Models;
Expand All @@ -11,7 +12,6 @@ namespace Conduit.Tests.Integration.Follows.Handlers;
[TestFixture]
public class FollowUserHandlerIntegrationTest : CouchbaseIntegrationTest
{
private FollowUserRequest _request;
private FollowUserHandler _handler;
private IConduitFollowsCollectionProvider _followsCollectionProvider;
private IConduitUsersCollectionProvider _usersCollectionProvider;
Expand All @@ -35,7 +35,7 @@ public async Task SetUp()
_usersCollectionProvider = ServiceProvider.GetRequiredService<IConduitUsersCollectionProvider>();

// setup handler and dependencies
var authService = new AuthService();
var authService = AuthServiceHelper.Create();
_handler = new FollowUserHandler(
new UserDataService(_usersCollectionProvider, authService),
new FollowsDataService(_followsCollectionProvider, authService),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Conduit.Tests.TestHelpers.Data;
using Conduit.Tests.TestHelpers;
using Conduit.Tests.TestHelpers.Data;
using Conduit.Web.Follows.Handlers;
using Conduit.Web.Follows.Services;
using Conduit.Web.Models;
Expand Down Expand Up @@ -35,7 +36,7 @@ public async Task SetUp()
_usersCollectionProvider = ServiceProvider.GetRequiredService<IConduitUsersCollectionProvider>();

// setup handler and dependencies
var authService = new AuthService();
var authService = AuthServiceHelper.Create();
_handler = new UnfollowUserHandler(
new UserDataService(_usersCollectionProvider, authService),
new FollowsDataService(_followsCollectionProvider, authService),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Conduit.Tests.TestHelpers.Data;
using Conduit.Tests.TestHelpers;
using Conduit.Tests.TestHelpers.Data;
using Conduit.Web.Models;
using Conduit.Web.Users.Handlers;
using Conduit.Web.Users.Services;
Expand Down Expand Up @@ -27,7 +28,7 @@ public override async Task Setup()

// setup handler and dependencies
_usersCollectionProvider = ServiceProvider.GetRequiredService<IConduitUsersCollectionProvider>();
var authService = new AuthService();
var authService = AuthServiceHelper.Create();
_getCurrentUserHandler = new GetCurrentUserHandler(authService, new UserDataService(_usersCollectionProvider, authService));
}

Expand All @@ -39,7 +40,7 @@ public async Task GetCurrentUserRequestHandler_Is_Successful()
// arrange the request
var email = $"valid{Path.GetRandomFileName()}@example.net";
var username = $"valid{Path.GetRandomFileName()}";
var fakeToken = new AuthService().GenerateJwtToken(email, username);
var fakeToken = AuthServiceHelper.Create().GenerateJwtToken(email, username);
var request = new GetCurrentUserRequest(fakeToken);

// arrange the current user already in the database
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Conduit.Tests.TestHelpers.Data;
using Conduit.Tests.TestHelpers;
using Conduit.Tests.TestHelpers.Data;
using Conduit.Web.Follows.Services;
using Conduit.Web.Models;
using Conduit.Web.Users.Handlers;
Expand Down Expand Up @@ -31,7 +32,7 @@ public override async Task Setup()
// setup handler
_usersCollectionProvider = ServiceProvider.GetRequiredService<IConduitUsersCollectionProvider>();
_followsCollectionProvider = ServiceProvider.GetRequiredService<IConduitFollowsCollectionProvider>();
_authService = new AuthService();
_authService = AuthServiceHelper.Create();
_getProfileHandler = new GetProfileHandler(new UserDataService(_usersCollectionProvider, _authService),
new GetProfileRequestValidator(),
new FollowsDataService(_followsCollectionProvider, _authService));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Conduit.Tests.TestHelpers.Data;
using Conduit.Tests.TestHelpers;
using Conduit.Tests.TestHelpers.Data;
using Conduit.Tests.TestHelpers.Dto;
using Conduit.Web.Models;
using Conduit.Web.Users.Handlers;
Expand Down Expand Up @@ -29,7 +30,7 @@ public override async Task Setup()
_usersCollectionProvider = ServiceProvider.GetRequiredService<IConduitUsersCollectionProvider>();

// arrange the handler
var authService = new AuthService();
var authService = AuthServiceHelper.Create();
_loginRequestHandler = new LoginRequestHandler(authService,
new LoginRequestValidator(),
new UserDataService(_usersCollectionProvider, authService));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Couchbase.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Conduit.Tests.TestHelpers.Data;
using Conduit.Tests.TestHelpers;

namespace Conduit.Tests.Integration.Users.Handlers;

Expand All @@ -29,7 +30,7 @@ public override async Task Setup()

// setup the handler and dependencies
_usersCollectionProvider = ServiceProvider.GetRequiredService<IConduitUsersCollectionProvider>();
var authService = new AuthService();
var authService = AuthServiceHelper.Create();
_registrationRequestHandler = new RegistrationRequestHandler(authService, new RegistrationRequestValidator(new SharedUserValidator<RegistrationUserSubmitModel>(), new UserDataService(_usersCollectionProvider, authService)), new UserDataService(_usersCollectionProvider, authService));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Conduit.Tests.TestHelpers.Data;
using Conduit.Tests.TestHelpers;
using Conduit.Tests.TestHelpers.Data;
using Conduit.Tests.TestHelpers.Dto;
using Conduit.Web.Models;
using Conduit.Web.Users.Handlers;
Expand Down Expand Up @@ -29,10 +30,11 @@ public override async Task Setup()
_usersCollectionProvider = ServiceProvider.GetRequiredService<IConduitUsersCollectionProvider>();

// arrange handler and dependencies
var authService = AuthServiceHelper.Create();
_handler = new UpdateUserHandler(
new UpdateUserRequestValidator(new UserDataService(_usersCollectionProvider, new AuthService()), new SharedUserValidator<UpdateUserViewModelUser>()),
new AuthService(),
new UserDataService(_usersCollectionProvider, new AuthService()));
new UpdateUserRequestValidator(new UserDataService(_usersCollectionProvider, authService), new SharedUserValidator<UpdateUserViewModelUser>()),
authService,
new UserDataService(_usersCollectionProvider, authService));
}

[Test]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Conduit.Tests.TestHelpers.Data;
using Conduit.Tests.TestHelpers;
using Conduit.Tests.TestHelpers.Data;
using Conduit.Web.Models;
using Conduit.Web.Users.Services;
using Couchbase.Extensions.DependencyInjection;
Expand All @@ -25,7 +26,7 @@ public override async Task Setup()
});

_usersCollectionProvider = ServiceProvider.GetRequiredService<IConduitUsersCollectionProvider>();
_userDataService = new UserDataService(_usersCollectionProvider, new AuthService());
_userDataService = new UserDataService(_usersCollectionProvider, AuthServiceHelper.Create());
}

[Test]
Expand Down
20 changes: 20 additions & 0 deletions Conduit/Conduit.Tests/TestHelpers/AuthServiceHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Conduit.Web.Users.Services;
using Microsoft.Extensions.Options;

namespace Conduit.Tests.TestHelpers;

public static class AuthServiceHelper
{
public static AuthService Create()
{
// the issuer, audience, securitykey are hardcoded values
// only meant for testing. Do not use these for your actual secrets!
return new AuthService(new OptionsWrapper<JwtSecrets>(
new JwtSecrets()
{
Issuer = "ConduitAspNetCouchbase_Issuer",
Audience = "ConduitAspNetCouchbase_Audience",
SecurityKey = "6B{DqP5aT,3b&!YRgk29m@j$L7uvnxE"
}));
}
}
5 changes: 3 additions & 2 deletions Conduit/Conduit.Tests/TestHelpers/Data/UserHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ public static User CreateUser(
image ??= "http://example.net/" + Path.GetRandomFileName() + ".jpg";
password ??= "ValidPassword1#-" + Path.GetRandomFileName();

var salt = new AuthService().GenerateSalt();
var authService = AuthServiceHelper.Create();
var salt = authService.GenerateSalt();

var user = new User
{
Email = email,
Password = new AuthService().HashPassword(password, salt),
Password = authService.HashPassword(password, salt),
PasswordSalt = salt,
Bio = bio,
Image = image,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Conduit.Web.Models;
using Conduit.Tests.TestHelpers;
using Conduit.Web.Models;
using Conduit.Web.Users.Handlers;
using Conduit.Web.Users.Services;
using Moq;
Expand Down Expand Up @@ -26,7 +27,7 @@ public async Task ValidToken_is_Handled()
// arrange request from API
var email = "[email protected]";
var username = "doesntmatter";
var fakeToken = new AuthService().GenerateJwtToken(email, username);
var fakeToken = AuthServiceHelper.Create().GenerateJwtToken(email, username);
var request = new GetCurrentUserRequest(fakeToken);

// arrange user that would be in db
Expand Down Expand Up @@ -90,7 +91,7 @@ public async Task UserIsMissingFromDatabase_is_Handled()
// arrange
// arrange request from API
var email = "[email protected]";
var fakeToken = new AuthService().GenerateJwtToken(email, "doesntmatter");
var fakeToken = AuthServiceHelper.Create().GenerateJwtToken(email, "doesntmatter");
var request = new GetCurrentUserRequest(fakeToken);


Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Conduit.Tests.TestHelpers.Data;
using Conduit.Tests.TestHelpers;
using Conduit.Tests.TestHelpers.Data;
using Conduit.Web.Follows.Services;
using Conduit.Web.Models;
using Conduit.Web.Users.Handlers;
Expand Down Expand Up @@ -29,7 +30,7 @@ public void SetUp()
public async Task Profile_for_a_user_following_the_user_in_the_profile(bool isUserFollowing)
{
// arrange
var currentUserToken = new AuthService().GenerateJwtToken("[email protected]", "MattGroves");
var currentUserToken = AuthServiceHelper.Create().GenerateJwtToken("[email protected]", "MattGroves");

// arrange for user to NOT be followed
var user = UserHelper.CreateUser(username: "SurlyDev");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace Conduit.Tests.Unit.Users.Services.AuthService;
using Conduit.Tests.TestHelpers;

namespace Conduit.Tests.Unit.Users.Services.AuthService;

[TestFixture]
public class GetTokenFromHeaderTests
Expand All @@ -8,7 +10,7 @@ public class GetTokenFromHeaderTests
public async Task Can_parse_header_down_to_just_token_value(string raw, string expectedParsed)
{
// arrange
var authService = new Web.Users.Services.AuthService();
var authService = AuthServiceHelper.Create();

// act
var result = authService.GetTokenFromHeader(raw);
Expand Down
14 changes: 9 additions & 5 deletions Conduit/Conduit.Web/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Couchbase.Extensions.DependencyInjection;
using FluentValidation;
using Microsoft.OpenApi.Models;
using Microsoft.Extensions.Configuration;

namespace Conduit.Web
{
Expand All @@ -28,7 +29,7 @@ public static void Main(string[] args)

builder.Services.AddConduitSwaggerSetup();

builder.Services.AddConduitAuthenticationSetup();
builder.Services.AddConduitAuthenticationSetup(builder.Configuration);

builder.Services.AddConduitServiceDependencies(builder.Configuration);

Expand Down Expand Up @@ -92,8 +93,11 @@ public static class AuthenticationSetupExtensions
/// <summary>
/// Add JWT authentication
/// </summary>
public static void AddConduitAuthenticationSetup(this IServiceCollection @this)
public static void AddConduitAuthenticationSetup(this IServiceCollection @this,
ConfigurationManager config)
{
@this.Configure<JwtSecrets>(config.GetSection("JwtSecrets"));

@this.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
Expand All @@ -103,9 +107,9 @@ public static void AddConduitAuthenticationSetup(this IServiceCollection @this)
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "ConduitAspNetCouchbase_Issuer", // Replace with your issuer
ValidAudience = "ConduitAspNetCouchbase_Audience", // Replace with your audience
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("6B{DqP5aT,3b&!YRgk29m@j$L7uvnxE")) // Replace with your secret key
ValidIssuer = config["JwtSecrets:Issuer"],
ValidAudience = config["JwtSecrets:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes( config["JwtSecrets:SecurityKey"]))
};
options.Events = new JwtBearerEvents()
{
Expand Down
13 changes: 10 additions & 3 deletions Conduit/Conduit.Web/Users/Services/AuthService.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;

namespace Conduit.Web.Users.Services;

public class AuthService : IAuthService
{
private readonly JwtSecrets _jwtSecrets;
private const string CLAIMTYPE_USERNAME = "Username";

public AuthService(IOptions<JwtSecrets> jwtSecrets)
{
_jwtSecrets = jwtSecrets.Value;
}

public string GenerateJwtToken(string email, string username)
{
// TODO: put username in claim too?
Expand All @@ -19,12 +26,12 @@ public string GenerateJwtToken(string email, string username)
new Claim(CLAIMTYPE_USERNAME, username)
};

var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("6B{DqP5aT,3b&!YRgk29m@j$L7uvnxE"));
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSecrets.SecurityKey));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

var token = new JwtSecurityToken(
issuer: "ConduitAspNetCouchbase_Issuer",
audience: "ConduitAspNetCouchbase_Audience",
issuer: _jwtSecrets.Issuer,
audience: _jwtSecrets.Audience,
claims: claims,
expires: DateTime.UtcNow.AddHours(4), // Set the token expiration time
signingCredentials: creds
Expand Down
8 changes: 8 additions & 0 deletions Conduit/Conduit.Web/Users/Services/JwtSecrets.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Conduit.Web.Users.Services;

public class JwtSecrets
{
public string Issuer { get; set; }
public string Audience { get; set; }
public string SecurityKey { get; set; }
}
7 changes: 6 additions & 1 deletion Conduit/Conduit.Web/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@
}
},
"AllowedHosts": "*",
"JwtSecrets": {
"Issuer": "<put issuer here>",
"Audience": "<put audience here>",
"SecurityKey": "<put a security key here>"
},
"Couchbase": {
"//I would recommend using User Secrets and environment variables instead of appsettings.json" : "comment",
"ConnectionString": "couchbases://cb.<capella connection string>.cloud.couchbase.com",
"Username": "<put username here>",
"Password": "<put password here",
"BucketName": "Conduit-appsettings.json",
"BucketName": "Conduit",
"ScopeName": "_default",
"UsersCollectionName": "Users",
"FollowsCollectionName": "Follows"
Expand Down

0 comments on commit 5320b83

Please sign in to comment.