Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save hlaueriksson/0144c69bb6c78a8ecb8a8874a7aa1a29 to your computer and use it in GitHub Desktop.
Save hlaueriksson/0144c69bb6c78a8ecb8a8874a7aa1a29 to your computer and use it in GitHub Desktop.
2017-03-31-secure-and-explore-aspnet-core-web-apis
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
},
"TokenOptions": {
"Audience": "http://localhost:50480",
"Issuer": "ConductOfCode",
"SigningKey": "cc4435685b40b2e9ddcb357fd79423b2d8e293b897d86f5336cb61c5fd31c9a3"
}
}
using System.IdentityModel.Tokens.Jwt;
using ConductOfCode.Extensions;
using ConductOfCode.Models;
using ConductOfCode.Options;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
namespace ConductOfCode.Controllers
{
[Route("api/[controller]")]
public class AuthenticationController : Controller
{
private TokenOptions Options { get; }
public AuthenticationController(IOptions<TokenOptions> options)
{
Options = options.Value;
}
[HttpPost("[action]")]
public TokenResponse Token([FromBody]TokenRequest request)
{
// TODO: Authenticate request
var token = new JwtSecurityToken(
audience: Options.Audience,
issuer: Options.Issuer,
expires: Options.GetExpiration(),
signingCredentials: Options.GetSigningCredentials());
return new TokenResponse
{
token_type = Options.Type,
access_token = new JwtSecurityTokenHandler().WriteToken(token),
expires_in = (int)Options.ValidFor.TotalSeconds
};
}
}
}
(function () {
$(function () {
var tokenUi = '<div class="input">' +
'<h2>Authenticate</h2>' +
'<input placeholder="Username" id="input_username" name="username" type="text" size="25">' +
'<input placeholder="Password" id="input_password" name="password" type="password" size="25">' +
'<input id="input_authenticate" name="authenticate" type="button" value="Get token">' +
'</div>';
$(tokenUi).insertBefore("#resources_container");
$("#input_authenticate").click(function () {
var username = $("#input_username").val();
var password = $("#input_password").val();
getToken(username, password);
});
});
function getToken(username, password) {
var request = { username: username, password: password };
$.ajax({
contentType: "application/json",
type: "post",
url: "/api/Authentication/Token",
dataType: "json",
data: request,
success: function (data) {
addAuthorization(data.token_type + " " + data.access_token);
}
});
};
function addAuthorization(key) {
window.swaggerUi.api.clientAuthorizations.add("key", new SwaggerClient.ApiKeyAuthorization("Authorization", key, "header"));
};
})();
(function () {
$(function () {
var tokenUi = '<div class="input">' +
'<h2>Authenticate</h2>' +
'<input placeholder="Audience" id="input_audience" name="audience" type="text" size="70"><br />' +
'<input placeholder="Issuer" id="input_issuer" name="issuer" type="text" size="70"><br />' +
'<input placeholder="Signing key" id="input_signing_key" name="signing_key" type="text" size="70">' +
'</div>';
$(tokenUi).insertBefore("#resources_container");
$(".submit").click(function () {
var audience = $("#input_audience").val();
var issuer = $("#input_issuer").val();
var signingKey = $("#input_signing_key").val();
var token = generateToken(audience, issuer, signingKey);
addAuthorization("Bearer " + token);
});
});
function generateToken(audience, issuer, signingKey) {
var exp = Date.now() / 1000 | 0;
var header = { 'alg': 'HS256', 'typ': 'JWT' };
var payload = { 'aud': audience, 'iss': issuer, 'exp': exp };
var unsignedToken = base64Object(header) + "." + base64Object(payload);
var signature = CryptoJS.HmacSHA256(unsignedToken, signingKey).toString(CryptoJS.enc.Base64);
var token = unsignedToken + "." + signature;
return removeIllegalCharacters(token);
};
function base64Object(input) {
var inputWords = CryptoJS.enc.Utf8.parse(JSON.stringify(input));
var base64 = CryptoJS.enc.Base64.stringify(inputWords);
var output = removeIllegalCharacters(base64);
return output;
};
function removeIllegalCharacters(input) {
return input
.replace(/=/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_');
};
function addAuthorization(key) {
window.swaggerUi.api.clientAuthorizations.add("key", new SwaggerClient.ApiKeyAuthorization("Authorization", key, "header"));
};
})();
var authorize = function () {
var audience = postman.getEnvironmentVariable("Audience");
var issuer = postman.getEnvironmentVariable("Issuer");
var signingKey = postman.getEnvironmentVariable("SigningKey");
var token = generateToken(audience, issuer, signingKey);
addAuthorization("Bearer " + token);
};
function generateToken(audience, issuer, signingKey) {
var exp = Date.now() / 1000 | 0;
var header = { 'alg': 'HS256', 'typ': 'JWT' };
var payload = { 'aud': audience, 'iss': issuer, 'exp': exp };
var unsignedToken = base64Object(header) + "." + base64Object(payload);
var signature = CryptoJS.HmacSHA256(unsignedToken, signingKey).toString(CryptoJS.enc.Base64);
var token = unsignedToken + "." + signature;
return removeIllegalCharacters(token);
};
function base64Object(input) {
var inputWords = CryptoJS.enc.Utf8.parse(JSON.stringify(input));
var base64 = CryptoJS.enc.Base64.stringify(inputWords);
var output = removeIllegalCharacters(base64);
return output;
};
function removeIllegalCharacters(input) {
return input
.replace(/=/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_');
};
function addAuthorization(value) {
postman.setGlobalVariable('Authorization', value);
};
using System;
using System.Collections.Generic;
using System.Linq;
using ConductOfCode.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace ConductOfCode.Controllers
{
[Authorize]
[Route("api/[controller]")]
public class StackController : Controller
{
private Stack<Item> Stack { get; }
public StackController(Stack<Item> stack)
{
Stack = stack;
}
/// <summary>Gets the number of elements contained in the Stack.</summary>
/// <returns>The number of elements contained in the Stack.</returns>
[HttpGet("[action]")]
[SwaggerResponse(200, typeof(int))]
public int Count()
{
return Stack.Count;
}
/// <summary>Removes all objects from the Stack.</summary>
[HttpDelete("[action]")]
[SwaggerResponse(200, typeof(void))]
public void Clear()
{
Stack.Clear();
}
/// <summary>Determines whether an element is in the Stack.</summary>
/// <returns>true if <paramref name="item" /> is found in the Stack; otherwise, false.</returns>
/// <param name="item">The object to locate in the Stack.</param>
[HttpPost("[action]")]
[SwaggerResponse(200, typeof(bool))]
public bool Contains([FromBody] Item item)
{
return Stack.Any(x => x.Value == item.Value);
}
/// <summary>Returns the object at the top of the Stack without removing it.</summary>
/// <returns>The object at the top of the Stack.</returns>
[HttpGet("[action]")]
[SwaggerResponse(200, typeof(Item))]
[SwaggerResponse(400, typeof(Error))]
public IActionResult Peek()
{
try
{
return Ok(Stack.Peek());
}
catch (InvalidOperationException ex)
{
return BadRequest(new Error(ex));
}
}
/// <summary>Removes and returns the object at the top of the Stack.</summary>
/// <returns>The object removed from the top of the Stack.</returns>
[HttpGet("[action]")]
[SwaggerResponse(200, typeof(Item))]
[SwaggerResponse(400, typeof(Error))]
public IActionResult Pop()
{
try
{
return Ok(Stack.Pop());
}
catch (InvalidOperationException ex)
{
return BadRequest(new Error(ex));
}
}
/// <summary>Inserts an object at the top of the Stack.</summary>
/// <param name="item">The object to push onto the Stack.</param>
[HttpPost("[action]")]
[SwaggerResponse(200, typeof(void))]
public void Push([FromBody] Item item)
{
Stack.Push(item);
}
/// <summary>Copies the Stack to a new array.</summary>
/// <returns>A new array containing copies of the elements of the Stack.</returns>
[HttpGet("[action]")]
[SwaggerResponse(200, typeof(Item[]))]
public Item[] ToArray()
{
return Stack.ToArray();
}
}
}
using System.Collections.Generic;
using ConductOfCode.Extensions;
using ConductOfCode.Models;
using ConductOfCode.Options;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Swashbuckle.AspNetCore.Swagger;
namespace ConductOfCode
{
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
services.AddSingleton(new Stack<Item>());
services.AddOptions();
services.Configure<TokenOptions>(Configuration.GetSection(nameof(TokenOptions)));
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "ConductOfCode", Version = "v1" });
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "ConductOfCode");
c.InjectOnCompleteJavaScript("/swagger-ui/authorization1.js");
//c.InjectOnCompleteJavaScript("https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.min.js"); // https://cdnjs.com/libraries/crypto-js
//c.InjectOnCompleteJavaScript("/swagger-ui/authorization2.js");
});
app.UseStaticFiles();
var options = Configuration.GetSection(nameof(TokenOptions)).Get<TokenOptions>();
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
TokenValidationParameters =
{
ValidAudience = options.Audience,
ValidIssuer = options.Issuer,
IssuerSigningKey = options.GetSymmetricSecurityKey()
}
});
app.UseMvc();
}
}
}
namespace ConductOfCode.Models
{
public class TokenRequest
{
public string Username { get; set; }
public string Password { get; set; }
}
public class TokenResponse
{
public string token_type { get; set; }
public string access_token { get; set; }
public int expires_in { get; set; }
}
}
using System;
namespace ConductOfCode.Options
{
public class TokenOptions
{
public string Type { get; set; } = "Bearer";
public TimeSpan ValidFor { get; set; } = TimeSpan.FromHours(1);
public string Audience { get; set; }
public string Issuer { get; set; }
public string SigningKey { get; set; }
}
}
using System;
using System.Text;
using ConductOfCode.Models;
using ConductOfCode.Options;
using Microsoft.IdentityModel.Tokens;
namespace ConductOfCode.Extensions
{
public static class TokenOptionsExtensions
{
public static DateTime GetExpiration(this TokenOptions options) => DateTime.UtcNow.Add(options.ValidFor);
public static SigningCredentials GetSigningCredentials(this TokenOptions options) => new SigningCredentials(options.GetSymmetricSecurityKey(), SecurityAlgorithms.HmacSha256);
public static SymmetricSecurityKey GetSymmetricSecurityKey(this TokenOptions options) => new SymmetricSecurityKey(options.GetSigningKeyBytes());
private static byte[] GetSigningKeyBytes(this TokenOptions options) => Encoding.ASCII.GetBytes(options.SigningKey);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment