Skip to content

Commit

Permalink
Add guest authorization
Browse files Browse the repository at this point in the history
  • Loading branch information
stepanbenes committed Apr 22, 2021
1 parent 7a450e9 commit a4dedcf
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 52 deletions.
2 changes: 1 addition & 1 deletion ClientApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ static async Task Main(string[] args)

Console.WriteLine("Authenticating...");

var tokenResponse = await nopApiClient.RequestToken(Username: args[1], Password: args[2]);
var tokenResponse = await nopApiClient.RequestToken(new TokenRequest { Username = args[1], Password = args[2] });

if (tokenResponse is { AccessToken: string token, TokenType: var type })
{
Expand Down
53 changes: 39 additions & 14 deletions ClientApp/nopapi.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -7801,22 +7801,30 @@
"Token"
],
"operationId": "RequestToken",
"parameters": [
{
"name": "Username",
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "Password",
"in": "query",
"schema": {
"type": "string"
"requestBody": {
"content": {
"application/json-patch+json": {
"schema": {
"$ref": "#/components/schemas/TokenRequest"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/TokenRequest"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/TokenRequest"
}
},
"application/*+json": {
"schema": {
"$ref": "#/components/schemas/TokenRequest"
}
}
}
],
},
"responses": {
"200": {
"description": "Success",
Expand Down Expand Up @@ -11876,6 +11884,23 @@
},
"additionalProperties": false
},
"TokenRequest": {
"type": "object",
"properties": {
"guest": {
"type": "boolean"
},
"username": {
"type": "string",
"nullable": true
},
"password": {
"type": "string",
"nullable": true
}
},
"additionalProperties": false
},
"TokenResponse": {
"required": [
"access_token",
Expand Down
117 changes: 84 additions & 33 deletions Nop.Plugin.Api/Controllers/TokenController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using Nop.Core;
using Nop.Core.Domain.Customers;
using Nop.Plugin.Api.Configuration;
using Nop.Plugin.Api.Domain;
Expand All @@ -24,6 +25,7 @@ public class TokenController : Controller
private readonly ApiConfiguration _apiConfiguration;
private readonly ApiSettings _apiSettings;
private readonly ICustomerActivityService _customerActivityService;
private readonly IWorkContext _workContext;
private readonly ICustomerRegistrationService _customerRegistrationService;
private readonly ICustomerService _customerService;
private readonly CustomerSettings _customerSettings;
Expand All @@ -32,13 +34,15 @@ public TokenController(
ICustomerService customerService,
ICustomerRegistrationService customerRegistrationService,
ICustomerActivityService customerActivityService,
IWorkContext workContext,
CustomerSettings customerSettings,
ApiSettings apiSettings,
ApiConfiguration apiConfiguration)
{
_customerService = customerService;
_customerRegistrationService = customerRegistrationService;
_customerActivityService = customerActivityService;
_workContext = workContext;
_customerSettings = customerSettings;
_apiSettings = apiSettings;
_apiConfiguration = apiConfiguration;
Expand All @@ -49,55 +53,85 @@ public TokenController(
[ProducesResponseType(typeof(TokenResponse), (int)HttpStatusCode.OK)]
[ProducesResponseType(typeof(string), (int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(string), (int)HttpStatusCode.Forbidden)]
public async Task<IActionResult> Create(TokenRequest model)
public async Task<IActionResult> Create([FromBody] TokenRequest model)
{
User.FindFirstValue(ClaimTypes.NameIdentifier);
Customer customer;

if (string.IsNullOrEmpty(model.Username))
if (model.Guest)
{
return BadRequest("Missing username");
customer = await _workContext.GetCurrentCustomerAsync();

if (!await _customerService.IsGuestAsync(customer))
{
customer = await _customerService.InsertGuestCustomerAsync();
await _workContext.SetCurrentCustomerAsync(customer);
}

if (!await _customerService.IsInCustomerRoleAsync(customer, Constants.Roles.ApiRoleSystemName))
{
//add to 'ApiUserRole' role if not yet present
var apiRole = await _customerService.GetCustomerRoleBySystemNameAsync(Constants.Roles.ApiRoleSystemName);
if (apiRole == null)
throw new InvalidOperationException($"'{Constants.Roles.ApiRoleSystemName}' role could not be loaded");
await _customerService.AddCustomerRoleMappingAsync(new CustomerCustomerRoleMapping { CustomerId = customer.Id, CustomerRoleId = apiRole.Id });
}

// activity log
await _customerActivityService.InsertActivityAsync(customer, "Api.TokenRequest", "API token request for guest customer", customer);
}

if (string.IsNullOrEmpty(model.Password))
else
{
return BadRequest("Missing password");
}
if (string.IsNullOrEmpty(model.Username))
{
return BadRequest("Missing username");
}

var customer = await ValidateUserAsync(model);
if (string.IsNullOrEmpty(model.Password))
{
return BadRequest("Missing password");
}

customer = await ValidateUserAsync(model.Username, model.Password);

if (customer is null)
{
return StatusCode((int)HttpStatusCode.Forbidden, "Wrong username or password");
}

if (customer is null)
{
return StatusCode((int)HttpStatusCode.Forbidden, "Wrong username or password");
}

return Json(GenerateToken(customer));
}
var tokenResponse = GenerateToken(customer);

private Task<CustomerLoginResults> LoginCustomerAsync(TokenRequest model)
{
return _customerRegistrationService.ValidateCustomerAsync(model.Username, model.Password);
await _workContext.SetCurrentCustomerAsync(customer);

// activity log
await _customerActivityService.InsertActivityAsync(customer, "Api.TokenRequest", "API token request", customer);

return Json(tokenResponse);
}

private async Task<Customer> ValidateUserAsync(TokenRequest model)
#region Private methods

private async Task<Customer> ValidateUserAsync(string username, string password)
{
var result = await LoginCustomerAsync(model);
var result = await LoginCustomerAsync(username, password);

if (result == CustomerLoginResults.Successful)
{
var customer = await (_customerSettings.UsernamesEnabled
? _customerService.GetCustomerByUsernameAsync(model.Username)
: _customerService.GetCustomerByEmailAsync(model.Username));


//activity log
await _customerActivityService.InsertActivityAsync(customer, "Api.TokenRequest", "User API token request", customer);

? _customerService.GetCustomerByUsernameAsync(username)
: _customerService.GetCustomerByEmailAsync(username));
return customer;
}

return null;
}

private Task<CustomerLoginResults> LoginCustomerAsync(string username, string password)
{
return _customerRegistrationService.ValidateCustomerAsync(username, password);
}

private int GetTokenExpiryInDays()
{
return _apiSettings.TokenExpiryInDays <= 0
Expand All @@ -114,19 +148,34 @@ private TokenResponse GenerateToken(Customer customer)
{
new Claim(JwtRegisteredClaimNames.Nbf, currentTime.ToUnixTimeSeconds().ToString()),
new Claim(JwtRegisteredClaimNames.Exp, expiresInSeconds.ToString()),
new Claim(ClaimTypes.Email, customer.Email),
new Claim("CustomerId", customer.Id.ToString()),
new Claim(ClaimTypes.NameIdentifier, customer.CustomerGuid.ToString()),
_customerSettings.UsernamesEnabled
? new Claim(ClaimTypes.Name, customer.Username)
: new Claim(ClaimTypes.Name, customer.Email)
};

var signingCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_apiConfiguration.SecurityKey)),
SecurityAlgorithms.HmacSha256);
if (!string.IsNullOrEmpty(customer.Email))
{
claims.Add(new Claim(ClaimTypes.Email, customer.Email));
}

if (_customerSettings.UsernamesEnabled)
{
if (!string.IsNullOrEmpty(customer.Username))
{
claims.Add(new Claim(ClaimTypes.Name, customer.Username));
}
}
else
{
if (!string.IsNullOrEmpty(customer.Email))
{
claims.Add(new Claim(ClaimTypes.Email, customer.Email));
}
}

var signingCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_apiConfiguration.SecurityKey)), SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(new JwtHeader(signingCredentials), new JwtPayload(claims));
var accessToken = new JwtSecurityTokenHandler().WriteToken(token);


return new TokenResponse(accessToken, currentTime.UtcDateTime, expiresInSeconds)
{
CustomerId = customer.Id,
Expand All @@ -135,5 +184,7 @@ private TokenResponse GenerateToken(Customer customer)
TokenType = "Bearer"
};
}

#endregion
}
}
6 changes: 2 additions & 4 deletions Nop.Plugin.Api/Infrastructure/ApiStartup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,10 @@ public void ConfigureServices(IServiceCollection services, IConfiguration config
jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey =
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(apiConfig.SecurityKey)),
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(apiConfig.SecurityKey)),
ValidateIssuer = false, // ValidIssuer = "The name of the issuer",
ValidateAudience = false, // ValidAudience = "The name of the audience",
ValidateLifetime =
true, // validate the expiration and not before values in the token
ValidateLifetime = true, // validate the expiration and not before values in the token
ClockSkew = TimeSpan.FromMinutes(apiConfig.AllowedClockSkewInMinutes)
};
});
Expand Down
3 changes: 3 additions & 0 deletions Nop.Plugin.Api/Models/Authentication/TokenRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ namespace Nop.Plugin.Api.Models.Authentication
{
public class TokenRequest
{
[JsonProperty("guest")]
public bool Guest { get; set; }

[JsonProperty("username")]
public string Username { get; set; }

Expand Down

0 comments on commit a4dedcf

Please sign in to comment.