Repository: cornflourblue/aspnet-core-3-jwt-refresh-tokens-api
Branch: master
Commit: b333934aa436
Files: 19
Total size: 19.3 KB
Directory structure:
gitextract_gwyk5znm/
├── .gitignore
├── .vscode/
│ ├── launch.json
│ └── tasks.json
├── Controllers/
│ └── UsersController.cs
├── Entities/
│ ├── RefreshToken.cs
│ └── User.cs
├── Helpers/
│ ├── AppSettings.cs
│ └── DataContext.cs
├── LICENSE
├── Models/
│ ├── AuthenticateRequest.cs
│ ├── AuthenticateResponse.cs
│ └── RevokeTokenRequest.cs
├── Program.cs
├── README.md
├── Services/
│ └── UserService.cs
├── Startup.cs
├── WebApi.csproj
├── appsettings.Development.json
└── appsettings.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules
jspm_packages
typings
# Optional npm cache directory
.npm
# Optional REPL history
.node_repl_history
# .NET compiled files
bin
obj
================================================
FILE: .vscode/launch.json
================================================
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (web)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/bin/Debug/netcoreapp3.1/WebApi.dll",
"args": [],
"cwd": "${workspaceFolder}",
"stopAtEntry": false,
"internalConsoleOptions": "openOnSessionStart",
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}"
}
]
}
================================================
FILE: .vscode/tasks.json
================================================
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/WebApi.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
}
]
}
================================================
FILE: Controllers/UsersController.cs
================================================
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using WebApi.Services;
using WebApi.Models;
using Microsoft.AspNetCore.Http;
using System;
namespace WebApi.Controllers
{
[Authorize]
[ApiController]
[Route("[controller]")]
public class UsersController : ControllerBase
{
private IUserService _userService;
public UsersController(IUserService userService)
{
_userService = userService;
}
[AllowAnonymous]
[HttpPost("authenticate")]
public IActionResult Authenticate([FromBody] AuthenticateRequest model)
{
var response = _userService.Authenticate(model, ipAddress());
if (response == null)
return BadRequest(new { message = "Username or password is incorrect" });
setTokenCookie(response.RefreshToken);
return Ok(response);
}
[AllowAnonymous]
[HttpPost("refresh-token")]
public IActionResult RefreshToken()
{
var refreshToken = Request.Cookies["refreshToken"];
var response = _userService.RefreshToken(refreshToken, ipAddress());
if (response == null)
return Unauthorized(new { message = "Invalid token" });
setTokenCookie(response.RefreshToken);
return Ok(response);
}
[HttpPost("revoke-token")]
public IActionResult RevokeToken([FromBody] RevokeTokenRequest model)
{
// accept token from request body or cookie
var token = model.Token ?? Request.Cookies["refreshToken"];
if (string.IsNullOrEmpty(token))
return BadRequest(new { message = "Token is required" });
var response = _userService.RevokeToken(token, ipAddress());
if (!response)
return NotFound(new { message = "Token not found" });
return Ok(new { message = "Token revoked" });
}
[HttpGet]
public IActionResult GetAll()
{
var users = _userService.GetAll();
return Ok(users);
}
[HttpGet("{id}")]
public IActionResult GetById(int id)
{
var user = _userService.GetById(id);
if (user == null) return NotFound();
return Ok(user);
}
[HttpGet("{id}/refresh-tokens")]
public IActionResult GetRefreshTokens(int id)
{
var user = _userService.GetById(id);
if (user == null) return NotFound();
return Ok(user.RefreshTokens);
}
// helper methods
private void setTokenCookie(string token)
{
var cookieOptions = new CookieOptions
{
HttpOnly = true,
Expires = DateTime.UtcNow.AddDays(7)
};
Response.Cookies.Append("refreshToken", token, cookieOptions);
}
private string ipAddress()
{
if (Request.Headers.ContainsKey("X-Forwarded-For"))
return Request.Headers["X-Forwarded-For"];
else
return HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString();
}
}
}
================================================
FILE: Entities/RefreshToken.cs
================================================
using System;
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;
using Microsoft.EntityFrameworkCore;
namespace WebApi.Entities
{
[Owned]
public class RefreshToken
{
[Key]
[JsonIgnore]
public int Id { get; set; }
public string Token { get; set; }
public DateTime Expires { get; set; }
public bool IsExpired => DateTime.UtcNow >= Expires;
public DateTime Created { get; set; }
public string CreatedByIp { get; set; }
public DateTime? Revoked { get; set; }
public string RevokedByIp { get; set; }
public string ReplacedByToken { get; set; }
public bool IsActive => Revoked == null && !IsExpired;
}
}
================================================
FILE: Entities/User.cs
================================================
using System.Text.Json.Serialization;
using System.Collections.Generic;
namespace WebApi.Entities
{
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Username { get; set; }
[JsonIgnore]
public string Password { get; set; }
[JsonIgnore]
public List<RefreshToken> RefreshTokens { get; set; }
}
}
================================================
FILE: Helpers/AppSettings.cs
================================================
namespace WebApi.Helpers
{
public class AppSettings
{
public string Secret { get; set; }
}
}
================================================
FILE: Helpers/DataContext.cs
================================================
using Microsoft.EntityFrameworkCore;
using WebApi.Entities;
namespace WebApi.Helpers
{
public class DataContext : DbContext
{
public DbSet<User> Users { get; set; }
public DataContext(DbContextOptions<DataContext> options) : base(options) { }
}
}
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2020 Jason Watmore
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Models/AuthenticateRequest.cs
================================================
using System.ComponentModel.DataAnnotations;
namespace WebApi.Models
{
public class AuthenticateRequest
{
[Required]
public string Username { get; set; }
[Required]
public string Password { get; set; }
}
}
================================================
FILE: Models/AuthenticateResponse.cs
================================================
using System.Text.Json.Serialization;
using WebApi.Entities;
namespace WebApi.Models
{
public class AuthenticateResponse
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Username { get; set; }
public string JwtToken { get; set; }
[JsonIgnore] // refresh token is returned in http only cookie
public string RefreshToken { get; set; }
public AuthenticateResponse(User user, string jwtToken, string refreshToken)
{
Id = user.Id;
FirstName = user.FirstName;
LastName = user.LastName;
Username = user.Username;
JwtToken = jwtToken;
RefreshToken = refreshToken;
}
}
}
================================================
FILE: Models/RevokeTokenRequest.cs
================================================
namespace WebApi.Models
{
public class RevokeTokenRequest
{
public string Token { get; set; }
}
}
================================================
FILE: Program.cs
================================================
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace WebApi
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>()
.UseUrls("http://localhost:4000");
});
}
}
================================================
FILE: README.md
================================================
# aspnet-core-3-jwt-refresh-tokens-api
ASP.NET Core 3.1 API - JWT Authentication with Refresh Tokens
Documentation and instructions available at https://jasonwatmore.com/post/2020/05/25/aspnet-core-3-api-jwt-authentication-with-refresh-tokens
================================================
FILE: Services/UserService.cs
================================================
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using WebApi.Models;
using WebApi.Entities;
using WebApi.Helpers;
namespace WebApi.Services
{
public interface IUserService
{
AuthenticateResponse Authenticate(AuthenticateRequest model, string ipAddress);
AuthenticateResponse RefreshToken(string token, string ipAddress);
bool RevokeToken(string token, string ipAddress);
IEnumerable<User> GetAll();
User GetById(int id);
}
public class UserService : IUserService
{
private DataContext _context;
private readonly AppSettings _appSettings;
public UserService(
DataContext context,
IOptions<AppSettings> appSettings)
{
_context = context;
_appSettings = appSettings.Value;
}
public AuthenticateResponse Authenticate(AuthenticateRequest model, string ipAddress)
{
var user = _context.Users.SingleOrDefault(x => x.Username == model.Username && x.Password == model.Password);
// return null if user not found
if (user == null) return null;
// authentication successful so generate jwt and refresh tokens
var jwtToken = generateJwtToken(user);
var refreshToken = generateRefreshToken(ipAddress);
// save refresh token
user.RefreshTokens.Add(refreshToken);
_context.Update(user);
_context.SaveChanges();
return new AuthenticateResponse(user, jwtToken, refreshToken.Token);
}
public AuthenticateResponse RefreshToken(string token, string ipAddress)
{
var user = _context.Users.SingleOrDefault(u => u.RefreshTokens.Any(t => t.Token == token));
// return null if no user found with token
if (user == null) return null;
var refreshToken = user.RefreshTokens.Single(x => x.Token == token);
// return null if token is no longer active
if (!refreshToken.IsActive) return null;
// replace old refresh token with a new one and save
var newRefreshToken = generateRefreshToken(ipAddress);
refreshToken.Revoked = DateTime.UtcNow;
refreshToken.RevokedByIp = ipAddress;
refreshToken.ReplacedByToken = newRefreshToken.Token;
user.RefreshTokens.Add(newRefreshToken);
_context.Update(user);
_context.SaveChanges();
// generate new jwt
var jwtToken = generateJwtToken(user);
return new AuthenticateResponse(user, jwtToken, newRefreshToken.Token);
}
public bool RevokeToken(string token, string ipAddress)
{
var user = _context.Users.SingleOrDefault(u => u.RefreshTokens.Any(t => t.Token == token));
// return false if no user found with token
if (user == null) return false;
var refreshToken = user.RefreshTokens.Single(x => x.Token == token);
// return false if token is not active
if (!refreshToken.IsActive) return false;
// revoke token and save
refreshToken.Revoked = DateTime.UtcNow;
refreshToken.RevokedByIp = ipAddress;
_context.Update(user);
_context.SaveChanges();
return true;
}
public IEnumerable<User> GetAll()
{
return _context.Users;
}
public User GetById(int id)
{
return _context.Users.Find(id);
}
// helper methods
private string generateJwtToken(User user)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, user.Id.ToString())
}),
Expires = DateTime.UtcNow.AddMinutes(15),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
private RefreshToken generateRefreshToken(string ipAddress)
{
using(var rngCryptoServiceProvider = new RNGCryptoServiceProvider())
{
var randomBytes = new byte[64];
rngCryptoServiceProvider.GetBytes(randomBytes);
return new RefreshToken
{
Token = Convert.ToBase64String(randomBytes),
Expires = DateTime.UtcNow.AddDays(7),
Created = DateTime.UtcNow,
CreatedByIp = ipAddress
};
}
}
}
}
================================================
FILE: Startup.cs
================================================
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using WebApi.Helpers;
using WebApi.Services;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using WebApi.Entities;
using System;
namespace WebApi
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// in memory database used for simplicity, change to a real db for production applications
services.AddDbContext<DataContext>(x => x.UseInMemoryDatabase("TestDb"));
services.AddCors();
services.AddControllers().AddJsonOptions(x => x.JsonSerializerOptions.IgnoreNullValues = true);
// configure strongly typed settings objects
var appSettingsSection = Configuration.GetSection("AppSettings");
services.Configure<AppSettings>(appSettingsSection);
// configure jwt authentication
var appSettings = appSettingsSection.Get<AppSettings>();
var key = Encoding.ASCII.GetBytes(appSettings.Secret);
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false,
// set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later)
ClockSkew = TimeSpan.Zero
};
});
// configure DI for application services
services.AddScoped<IUserService, UserService>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, DataContext context)
{
// add hardcoded test user to db on startup
// plain text password is used for simplicity, hashed passwords should be used in production applications
context.Users.Add(new User { FirstName = "Test", LastName = "User", Username = "test", Password = "test" });
context.SaveChanges();
app.UseRouting();
// global cors policy
app.UseCors(x => x
.SetIsOriginAllowed(origin => true)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(x => x.MapControllers());
}
}
}
================================================
FILE: WebApi.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="3.1.4" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.5.1" />
</ItemGroup>
</Project>
================================================
FILE: appsettings.Development.json
================================================
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}
================================================
FILE: appsettings.json
================================================
{
"AppSettings": {
"Secret": "THIS IS USED TO SIGN AND VERIFY JWT TOKENS, REPLACE IT WITH YOUR OWN SECRET, IT CAN BE ANY STRING"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
gitextract_gwyk5znm/ ├── .gitignore ├── .vscode/ │ ├── launch.json │ └── tasks.json ├── Controllers/ │ └── UsersController.cs ├── Entities/ │ ├── RefreshToken.cs │ └── User.cs ├── Helpers/ │ ├── AppSettings.cs │ └── DataContext.cs ├── LICENSE ├── Models/ │ ├── AuthenticateRequest.cs │ ├── AuthenticateResponse.cs │ └── RevokeTokenRequest.cs ├── Program.cs ├── README.md ├── Services/ │ └── UserService.cs ├── Startup.cs ├── WebApi.csproj ├── appsettings.Development.json └── appsettings.json
SYMBOL INDEX (41 symbols across 11 files)
FILE: Controllers/UsersController.cs
class UsersController (line 10) | [Authorize]
method UsersController (line 17) | public UsersController(IUserService userService)
method Authenticate (line 22) | [AllowAnonymous]
method RefreshToken (line 36) | [AllowAnonymous]
method RevokeToken (line 51) | [HttpPost("revoke-token")]
method GetAll (line 68) | [HttpGet]
method GetById (line 75) | [HttpGet("{id}")]
method GetRefreshTokens (line 84) | [HttpGet("{id}/refresh-tokens")]
method setTokenCookie (line 95) | private void setTokenCookie(string token)
method ipAddress (line 105) | private string ipAddress()
FILE: Entities/RefreshToken.cs
class RefreshToken (line 8) | [Owned]
FILE: Entities/User.cs
class User (line 6) | public class User
FILE: Helpers/AppSettings.cs
class AppSettings (line 3) | public class AppSettings
FILE: Helpers/DataContext.cs
class DataContext (line 6) | public class DataContext : DbContext
method DataContext (line 10) | public DataContext(DbContextOptions<DataContext> options) : base(optio...
FILE: Models/AuthenticateRequest.cs
class AuthenticateRequest (line 5) | public class AuthenticateRequest
FILE: Models/AuthenticateResponse.cs
class AuthenticateResponse (line 6) | public class AuthenticateResponse
method AuthenticateResponse (line 17) | public AuthenticateResponse(User user, string jwtToken, string refresh...
FILE: Models/RevokeTokenRequest.cs
class RevokeTokenRequest (line 3) | public class RevokeTokenRequest
FILE: Program.cs
class Program (line 6) | public class Program
method Main (line 8) | public static void Main(string[] args)
method CreateHostBuilder (line 13) | public static IHostBuilder CreateHostBuilder(string[] args) =>
FILE: Services/UserService.cs
type IUserService (line 16) | public interface IUserService
method Authenticate (line 18) | AuthenticateResponse Authenticate(AuthenticateRequest model, string ip...
method RefreshToken (line 19) | AuthenticateResponse RefreshToken(string token, string ipAddress);
method RevokeToken (line 20) | bool RevokeToken(string token, string ipAddress);
method GetAll (line 21) | IEnumerable<User> GetAll();
method GetById (line 22) | User GetById(int id);
class UserService (line 25) | public class UserService : IUserService
method UserService (line 30) | public UserService(
method Authenticate (line 38) | public AuthenticateResponse Authenticate(AuthenticateRequest model, st...
method RefreshToken (line 57) | public AuthenticateResponse RefreshToken(string token, string ipAddress)
method RevokeToken (line 84) | public bool RevokeToken(string token, string ipAddress)
method GetAll (line 105) | public IEnumerable<User> GetAll()
method GetById (line 110) | public User GetById(int id)
method generateJwtToken (line 117) | private string generateJwtToken(User user)
method generateRefreshToken (line 134) | private RefreshToken generateRefreshToken(string ipAddress)
FILE: Startup.cs
class Startup (line 16) | public class Startup
method Startup (line 18) | public Startup(IConfiguration configuration)
method ConfigureServices (line 26) | public void ConfigureServices(IServiceCollection services)
method Configure (line 66) | public void Configure(IApplicationBuilder app, IWebHostEnvironment env...
Condensed preview — 19 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (21K chars).
[
{
"path": ".gitignore",
"chars": 616,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Directory for instrumented libs generated by jscov"
},
{
"path": ".vscode/launch.json",
"chars": 1122,
"preview": "{\n // Use IntelliSense to learn about possible attributes.\n // Hover to view descriptions of existing attributes.\n"
},
{
"path": ".vscode/tasks.json",
"chars": 424,
"preview": "{\n \"version\": \"2.0.0\",\n \"tasks\": [\n {\n \"label\": \"build\",\n \"command\": \"dotnet\",\n "
},
{
"path": "Controllers/UsersController.cs",
"chars": 3262,
"preview": "using Microsoft.AspNetCore.Mvc;\nusing Microsoft.AspNetCore.Authorization;\nusing WebApi.Services;\nusing WebApi.Models;\nu"
},
{
"path": "Entities/RefreshToken.cs",
"chars": 751,
"preview": "using System;\nusing System.ComponentModel.DataAnnotations;\nusing System.Text.Json.Serialization;\nusing Microsoft.EntityF"
},
{
"path": "Entities/User.cs",
"chars": 459,
"preview": "using System.Text.Json.Serialization;\nusing System.Collections.Generic;\n\nnamespace WebApi.Entities\n{\n public class Us"
},
{
"path": "Helpers/AppSettings.cs",
"chars": 112,
"preview": "namespace WebApi.Helpers\n{\n public class AppSettings\n {\n public string Secret { get; set; }\n }\n}"
},
{
"path": "Helpers/DataContext.cs",
"chars": 276,
"preview": "using Microsoft.EntityFrameworkCore;\nusing WebApi.Entities;\n\nnamespace WebApi.Helpers\n{\n public class DataContext : D"
},
{
"path": "LICENSE",
"chars": 1080,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2020 Jason Watmore\n\nPermission is hereby granted, free of charge, to any person obt"
},
{
"path": "Models/AuthenticateRequest.cs",
"chars": 251,
"preview": "using System.ComponentModel.DataAnnotations;\n\nnamespace WebApi.Models\n{\n public class AuthenticateRequest\n {\n "
},
{
"path": "Models/AuthenticateResponse.cs",
"chars": 798,
"preview": "using System.Text.Json.Serialization;\nusing WebApi.Entities;\n\nnamespace WebApi.Models\n{\n public class AuthenticateRes"
},
{
"path": "Models/RevokeTokenRequest.cs",
"chars": 117,
"preview": "namespace WebApi.Models\n{\n public class RevokeTokenRequest\n {\n public string Token { get; set; }\n }\n}"
},
{
"path": "Program.cs",
"chars": 572,
"preview": "using Microsoft.AspNetCore.Hosting;\nusing Microsoft.Extensions.Hosting;\n\nnamespace WebApi\n{\n public class Program\n "
},
{
"path": "README.md",
"chars": 245,
"preview": "# aspnet-core-3-jwt-refresh-tokens-api\n\nASP.NET Core 3.1 API - JWT Authentication with Refresh Tokens\n\nDocumentation and"
},
{
"path": "Services/UserService.cs",
"chars": 5224,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.IdentityModel.Tokens.Jwt;\nusing System.Linq;\nusing System.S"
},
{
"path": "Startup.cs",
"chars": 3464,
"preview": "using Microsoft.AspNetCore.Builder;\nusing Microsoft.AspNetCore.Hosting;\nusing Microsoft.Extensions.Configuration;\nusing"
},
{
"path": "WebApi.csproj",
"chars": 438,
"preview": "<Project Sdk=\"Microsoft.NET.Sdk.Web\">\n <PropertyGroup>\n <TargetFramework>netcoreapp3.1</TargetFramework>\n </Proper"
},
{
"path": "appsettings.Development.json",
"chars": 167,
"preview": "{\n \"Logging\": {\n \"LogLevel\": {\n \"Default\": \"Debug\",\n \"System\": \"Information\",\n "
},
{
"path": "appsettings.json",
"chars": 360,
"preview": "{\n \"AppSettings\": {\n \"Secret\": \"THIS IS USED TO SIGN AND VERIFY JWT TOKENS, REPLACE IT WITH YOUR OWN SECRET, "
}
]
About this extraction
This page contains the full source code of the cornflourblue/aspnet-core-3-jwt-refresh-tokens-api GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 19 files (19.3 KB), approximately 4.4k tokens, and a symbol index with 41 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.