Skip to content

Commit da1fe1d

Browse files
Add sample for JWT
1 parent 6efb0d0 commit da1fe1d

File tree

5 files changed

+118
-0
lines changed

5 files changed

+118
-0
lines changed

Saturn.sln

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sample", "sample", "{511FB3
1111
EndProject
1212
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Saturn.Sample", "sample\Saturn.Sample\Saturn.Sample.fsproj", "{5FBE7B9D-4E1B-4ACA-B9A5-8DA88D6EF4C0}"
1313
EndProject
14+
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "JWTSample", "sample\JWTSample\JWTSample.fsproj", "{C2B9E879-D1EC-49C1-A86F-0D36202454D2}"
15+
EndProject
1416
Global
1517
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1618
Debug|Any CPU = Debug|Any CPU
@@ -48,9 +50,22 @@ Global
4850
{5FBE7B9D-4E1B-4ACA-B9A5-8DA88D6EF4C0}.Release|x64.Build.0 = Release|x64
4951
{5FBE7B9D-4E1B-4ACA-B9A5-8DA88D6EF4C0}.Release|x86.ActiveCfg = Release|x86
5052
{5FBE7B9D-4E1B-4ACA-B9A5-8DA88D6EF4C0}.Release|x86.Build.0 = Release|x86
53+
{C2B9E879-D1EC-49C1-A86F-0D36202454D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
54+
{C2B9E879-D1EC-49C1-A86F-0D36202454D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
55+
{C2B9E879-D1EC-49C1-A86F-0D36202454D2}.Debug|x64.ActiveCfg = Debug|x64
56+
{C2B9E879-D1EC-49C1-A86F-0D36202454D2}.Debug|x64.Build.0 = Debug|x64
57+
{C2B9E879-D1EC-49C1-A86F-0D36202454D2}.Debug|x86.ActiveCfg = Debug|x86
58+
{C2B9E879-D1EC-49C1-A86F-0D36202454D2}.Debug|x86.Build.0 = Debug|x86
59+
{C2B9E879-D1EC-49C1-A86F-0D36202454D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
60+
{C2B9E879-D1EC-49C1-A86F-0D36202454D2}.Release|Any CPU.Build.0 = Release|Any CPU
61+
{C2B9E879-D1EC-49C1-A86F-0D36202454D2}.Release|x64.ActiveCfg = Release|x64
62+
{C2B9E879-D1EC-49C1-A86F-0D36202454D2}.Release|x64.Build.0 = Release|x64
63+
{C2B9E879-D1EC-49C1-A86F-0D36202454D2}.Release|x86.ActiveCfg = Release|x86
64+
{C2B9E879-D1EC-49C1-A86F-0D36202454D2}.Release|x86.Build.0 = Release|x86
5165
EndGlobalSection
5266
GlobalSection(NestedProjects) = preSolution
5367
{8DBA089A-7C24-4E87-870B-E0774654F376} = {F2C8C347-845F-42E4-A702-7381C4B4087F}
5468
{5FBE7B9D-4E1B-4ACA-B9A5-8DA88D6EF4C0} = {511FB392-5714-4028-97F3-F883F81B43DB}
69+
{C2B9E879-D1EC-49C1-A86F-0D36202454D2} = {511FB392-5714-4028-97F3-F883F81B43DB}
5570
EndGlobalSection
5671
EndGlobal

sample/JWTSample/App.config

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<configuration>
3+
<startup>
4+
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
5+
</startup>
6+
</configuration>

sample/JWTSample/JWTSample.fs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
module JWTSample
2+
open System
3+
open Saturn.Application
4+
open Saturn.ControllerHelpers
5+
open System.Security.Claims
6+
open System.IdentityModel.Tokens.Jwt
7+
open Microsoft.IdentityModel.Tokens
8+
open Saturn.Router
9+
open Giraffe
10+
open Microsoft.AspNetCore.Http
11+
open Saturn.Pipeline
12+
13+
//Based on https://medium.com/@dsincl12/json-web-token-with-giraffe-and-f-4cebe1c3ef3b
14+
15+
let secret = "spadR2dre#u-ruBrE@TepA&*Uf@U"
16+
let issuer = "saturnframework.io"
17+
18+
[<CLIMutable>]
19+
type LoginViewModel =
20+
{
21+
Email : string
22+
Password : string
23+
}
24+
25+
[<CLIMutable>]
26+
type TokenResult =
27+
{
28+
Token : string
29+
}
30+
31+
let generateToken email =
32+
let claims = [|
33+
Claim(JwtRegisteredClaimNames.Sub, email);
34+
Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) |]
35+
claims
36+
|> Authentication.generateToken (secret, SecurityAlgorithms.HmacSha256) issuer (DateTime.UtcNow.AddHours(1.0))
37+
38+
let handleGetSecured =
39+
fun (next : HttpFunc) (ctx : HttpContext) ->
40+
let email = ctx.User.FindFirst ClaimTypes.NameIdentifier
41+
text ("User " + email.Value + " is authorized to access this resource.") next ctx
42+
43+
let handlePostToken =
44+
fun (next : HttpFunc) (ctx : HttpContext) ->
45+
task {
46+
let! model = ctx.BindJsonAsync<LoginViewModel>()
47+
48+
// authenticate user
49+
50+
let tokenResult = generateToken model.Email
51+
52+
return! json tokenResult next ctx
53+
}
54+
55+
let securedRouter = scope {
56+
pipe_through jwtAuthentication
57+
get "/" handleGetSecured
58+
}
59+
60+
let topRouter = scope {
61+
error_handler (setStatusCode 404 >=> text "Not Found")
62+
63+
post "/token" handlePostToken
64+
get "/" (text "public route")
65+
forward "/secured" securedRouter
66+
}
67+
68+
let app = application {
69+
use_jwt_authentication secret issuer
70+
71+
router topRouter
72+
url "http://0.0.0.0:8085/"
73+
}
74+
75+
[<EntryPoint>]
76+
let main _ =
77+
run app
78+
0

sample/JWTSample/JWTSample.fsproj

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
<TargetFramework>net461</TargetFramework>
5+
</PropertyGroup>
6+
<ItemGroup>
7+
<ProjectReference Include="../../src/Saturn/Saturn.fsproj">
8+
<Name>Saturn.fsproj</Name>
9+
</ProjectReference>
10+
</ItemGroup>
11+
<ItemGroup>
12+
<Compile Include="JWTSample.fs" />
13+
<None Include="App.config" />
14+
</ItemGroup>
15+
<Import Project="..\..\.paket\Paket.Restore.targets" />
16+
</Project>

sample/JWTSample/paket.references

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
FSharp.Core
2+
Giraffe
3+
Microsoft.AspNetCore

0 commit comments

Comments
 (0)