· 6 years ago · Jan 18, 2020, 06:08 PM
1using System;
2using System.Collections.Generic;
3using System.IO;
4using System.Linq;
5using System.Security.Claims;
6using System.Text.RegularExpressions;
7using System.Threading.Tasks;
8using System.Web;
9using Minimatch;
10
11namespace NancyAPI.Authentication
12{
13 public class StatelessAuth
14 {
15 private readonly IValidator validator;
16 private readonly StatelessAuthOptions statelessAuthOptions;
17 private readonly Func<IDictionary<string, object>, Task> nextFunc;
18 private const string ServerUser = "server.User";
19
20 public StatelessAuth(Func<IDictionary<string, object>, Task> nextFunc, IValidator validator, StatelessAuthOptions statelessAuthOptions)
21 {
22 this.nextFunc = nextFunc;
23 this.validator = validator;
24 this.statelessAuthOptions = statelessAuthOptions;
25 }
26
27 public Task Invoke(IDictionary<string, object> environment)
28 {
29 if (!environment.ContainsKey("owin.RequestPath"))
30 {
31 throw new ApplicationException("Invalid OWIN request. Expected owin.RequestPath.");
32 }
33
34 var path = Uri.UnescapeDataString((string)environment["owin.RequestPath"]);
35
36 if (statelessAuthOptions != null && statelessAuthOptions.IgnorePaths != null)
37 {
38 foreach(var ignorePath in statelessAuthOptions.IgnorePaths)
39 {
40 var mm = new Minimatcher(ignorePath, new Options() { IgnoreCase = true });
41
42 if (mm.IsMatch(path))
43 {
44 return nextFunc(environment);
45 }
46 }
47 }
48
49 var requestHeaders = (IDictionary<string, string[]>)environment["owin.RequestHeaders"];
50
51 ClaimsPrincipal validatedUser = new ClaimsPrincipal();
52
53 //Check for JWT Token
54 if (requestHeaders.ContainsKey("Authorization"))
55 {
56 //Get token from request header
57 var token = requestHeaders["Authorization"].FirstOrDefault();
58
59 if (!string.IsNullOrEmpty(token))
60 {
61 validatedUser = validator.ValidateUserFromToken(token);
62 }
63 else
64 {
65 return AuthChallengeResponse(environment);
66 }
67 }
68 //Check for API Key
69 else if(requestHeaders.ContainsKey("APIKEY"))
70 {
71 //Get API Key from request header
72 var key = requestHeaders["APIKEY"].FirstOrDefault();
73
74 if (!string.IsNullOrEmpty(key))
75 {
76 validatedUser = validator.ValidateUserFromKey(key);
77 }
78 else
79 {
80 return AuthChallengeResponse(environment);
81 }
82 }
83 else
84 {
85 return AuthChallengeResponse(environment);
86 }
87
88 //Check if a user was found
89 if (validatedUser == null)
90 {
91 //Maybe change response here?
92 return AuthChallengeResponse(environment);
93 }
94
95 //Check if server user exists
96 if (environment.ContainsKey(ServerUser))
97 {
98 environment[ServerUser] = validatedUser;
99 }
100 else
101 {
102 environment.Add(ServerUser, validatedUser);
103 }
104
105 return nextFunc(environment);
106
107 }
108
109 private Task AuthChallengeResponse(IDictionary<string, object> environment)
110 {
111 if (statelessAuthOptions != null && !string.IsNullOrWhiteSpace(statelessAuthOptions.WWWAuthenticateChallenge))
112 {
113 return nextFunc(environment);
114 }
115
116 environment["owin.ResponseStatusCode"] = 401;
117
118 if (statelessAuthOptions != null && !string.IsNullOrWhiteSpace(statelessAuthOptions.WWWAuthenticateChallenge))
119 {
120 var wwwauthenticatechallenge = statelessAuthOptions.WWWAuthenticateChallenge;
121
122 if(!environment.ContainsKey("owin.ResponseHeaders"))
123 {
124 environment.Add("owin.ResponseHeaders", new Dictionary<string, string[]>());
125 }
126
127 var responseHeaders = (IDictionary<string, string[]>)environment["owin.ResponseHeaders"];
128 responseHeaders.Add("WWW-Authenticate", new[] { wwwauthenticatechallenge });
129 }
130
131 return Task.FromResult(0);
132 }
133 }
134}