1
- using ServiceStack . Auth ;
1
+ using System . Collections . Concurrent ;
2
+ using ServiceStack . Auth ;
2
3
using AiServer . ServiceInterface ;
4
+ using ServiceStack . Configuration ;
5
+ using ServiceStack . Html ;
6
+ using ServiceStack . Web ;
3
7
4
8
[ assembly: HostingStartup ( typeof ( ConfigureAuth ) ) ]
5
9
@@ -23,4 +27,166 @@ public void Configure(IWebHostBuilder builder) => builder
23
27
using var db = HostContext . AppHost . GetDbConnection ( ) ;
24
28
appHost . GetPlugin < ApiKeysFeature > ( ) . InitSchema ( db ) ;
25
29
} ) ;
26
- }
30
+ }
31
+
32
+ public class ApiKeyCredentialsProvider : AuthProvider
33
+ {
34
+ public override string Type => AuthenticateService . CredentialsProvider ;
35
+ public ConcurrentDictionary < string , IApiKey > ValidApiKeys { get ; private set ; } = new ( ) ;
36
+
37
+ public ApiKeyCredentialsProvider ( )
38
+ {
39
+ Provider = AuthenticateService . CredentialsProvider ;
40
+ Sort = - 1 ;
41
+ Label = Provider . ToPascalCase ( ) ;
42
+ FormLayout =
43
+ [
44
+ Input . For < Authenticate > ( x => x . UserName , c =>
45
+ {
46
+ c . Label = "Display Name" ;
47
+ c . Required = true ;
48
+ } ) ,
49
+
50
+ Input . For < Authenticate > ( x => x . Password , c =>
51
+ {
52
+ c . Label = "API Key" ;
53
+ c . Type = "Password" ;
54
+ c . Required = true ;
55
+ } ) ,
56
+
57
+ Input . For < Authenticate > ( x => x . RememberMe )
58
+ ] ;
59
+ }
60
+
61
+ public override void Configure ( IServiceCollection services , AuthFeature authFeature )
62
+ {
63
+ }
64
+
65
+ public override void Register ( IAppHost appHost , AuthFeature authFeature )
66
+ {
67
+ var feature = appHost . GetPlugin < ApiKeysFeature > ( ) ;
68
+ if ( feature != null )
69
+ {
70
+ ValidApiKeys = feature . ValidApiKeys ;
71
+ }
72
+
73
+ appHost . PreRequestFilters . Add ( ( req , res ) =>
74
+ {
75
+ var session = req . GetSession ( ) ;
76
+ if ( session . IsAuthenticated && session is AuthUserSession { RequestTokenSecret : not null } authSession )
77
+ {
78
+ if ( appHost . Config . AdminAuthSecret == authSession . RequestTokenSecret )
79
+ {
80
+ req . Items [ Keywords . AuthSecret ] = appHost . Config . AdminAuthSecret ;
81
+ req . Items [ Keywords . Authorization ] = "Bearer " + appHost . Config . AdminAuthSecret ;
82
+ }
83
+ if ( ValidApiKeys . TryGetValue ( authSession . RequestTokenSecret , out var _ ) )
84
+ {
85
+ req . Items [ Keywords . Authorization ] = "Bearer " + authSession . RequestTokenSecret ;
86
+ }
87
+ }
88
+ } ) ;
89
+ }
90
+
91
+ public override async Task < object ? > AuthenticateAsync ( IServiceBase authService , IAuthSession ? session , Authenticate request , CancellationToken token = new ( ) )
92
+ {
93
+ var req = authService . Request ;
94
+ var authSecret = request . Password ;
95
+ var sessionId = session ? . Id ?? Guid . NewGuid ( ) . ToString ( "n" ) ;
96
+ session = null ;
97
+ if ( HostContext . Config . AdminAuthSecret != null && HostContext . Config . AdminAuthSecret == authSecret )
98
+ {
99
+ var authSession = HostContext . AssertPlugin < AuthFeature > ( ) . AuthSecretSession ;
100
+ session = new AuthUserSession {
101
+ Id = sessionId ,
102
+ DisplayName = authSession . DisplayName ,
103
+ UserName = authSession . UserName ,
104
+ UserAuthName = authSession . UserAuthName ,
105
+ AuthProvider = AuthenticateService . ApiKeyProvider ,
106
+ IsAuthenticated = authSession . IsAuthenticated ,
107
+ Roles = authSession . Roles ,
108
+ Permissions = authSession . Permissions ,
109
+ UserAuthId = authSession . UserAuthId ,
110
+ RequestTokenSecret = authSecret ,
111
+ } ;
112
+ }
113
+
114
+ var apiKey = await GetValidApiKeyAsync ( authSecret , req ) ;
115
+ if ( apiKey != null )
116
+ {
117
+ var dbApiKey = ( ApiKeysFeature . ApiKey ) apiKey ;
118
+ session = new AuthUserSession
119
+ {
120
+ Id = sessionId ,
121
+ DisplayName = request . UserName ,
122
+ UserName = dbApiKey . Name ,
123
+ UserAuthName = dbApiKey . Name ,
124
+ AuthProvider = AuthenticateService . ApiKeyProvider ,
125
+ IsAuthenticated = true ,
126
+ Roles = dbApiKey . Scopes ,
127
+ Permissions = [ ] ,
128
+ UserAuthId = $ "{ dbApiKey . Id } ",
129
+ RequestTokenSecret = apiKey . Key ,
130
+ } ;
131
+ }
132
+
133
+ if ( session != null )
134
+ {
135
+ session . IsAuthenticated = true ;
136
+ await SaveSessionAsync ( session , req , token : token ) ;
137
+
138
+ var response = new AuthenticateResponse
139
+ {
140
+ UserId = session . UserAuthId ,
141
+ UserName = session . UserName ,
142
+ SessionId = session . Id ,
143
+ DisplayName = session . DisplayName ,
144
+ ReferrerUrl = session . ReferrerUrl ,
145
+ AuthProvider = session . AuthProvider ,
146
+ } ;
147
+ return response ;
148
+ }
149
+
150
+ throw HttpError . Unauthorized ( ErrorMessages . ApiKeyInvalid . Localize ( authService . Request ) ) ;
151
+ }
152
+
153
+ public async Task < IApiKey ? > GetValidApiKeyAsync ( string token , IRequest request )
154
+ {
155
+ if ( string . IsNullOrEmpty ( token ) )
156
+ return null ;
157
+
158
+ var source = request . TryResolve < IApiKeySource > ( ) ;
159
+ if ( ValidApiKeys . TryGetValue ( token , out var apiKey ) )
160
+ return apiKey ;
161
+
162
+ apiKey = await source . GetApiKeyAsync ( token ) ;
163
+ if ( apiKey != null )
164
+ {
165
+ ValidApiKeys [ token ] = apiKey ;
166
+
167
+ if ( apiKey . HasScope ( RoleNames . Admin ) )
168
+ return apiKey ;
169
+
170
+ return apiKey ;
171
+ }
172
+
173
+ return null ;
174
+ }
175
+
176
+ async Task SaveSessionAsync ( IAuthSession session , IRequest httpReq , CancellationToken token )
177
+ {
178
+ session . LastModified = DateTime . UtcNow ;
179
+ httpReq . Items [ Keywords . Session ] = session ;
180
+
181
+ var sessionKey = SessionFeature . GetSessionKey ( session . Id ?? httpReq . GetOrCreateSessionId ( ) ) ;
182
+ await httpReq . GetCacheClientAsync ( ) . CacheSetAsync ( sessionKey , session , SessionFeature . DefaultSessionExpiry , token ) ;
183
+ }
184
+
185
+ public override bool IsAuthorized ( IAuthSession session , IAuthTokens tokens , Authenticate ? request = null )
186
+ {
187
+ if ( session is AuthUserSession { RequestTokenSecret : not null } userSession )
188
+ return HostContext . Config . AdminAuthSecret == userSession . RequestTokenSecret
189
+ || ValidApiKeys . ContainsKey ( userSession . RequestTokenSecret ) ;
190
+ return false ;
191
+ }
192
+ }
0 commit comments