10 |
11 |
12 |
13 |
14 | JWT Simple Server Playground
15 |
16 |
17 |
18 |
19 |
20 | Request token
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | Call api/test protected controller with token:
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | Refresh token service setup
39 |
40 |
41 |
42 |
43 |
Start refresh token service (every 5 seconds)
44 |
45 |
46 |
Token History
47 |
48 |
49 |
50 |
51 | Stop refresh token service
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
138 |
139 |
140 |
141 |
155 |
--------------------------------------------------------------------------------
/samples/SimpleServerEFCoreStorage/wwwroot/simple-server-client.js:
--------------------------------------------------------------------------------
1 | window["JwtSimpleServer"] =
2 | /******/ (function(modules) { // webpackBootstrap
3 | /******/ // The module cache
4 | /******/ var installedModules = {};
5 | /******/
6 | /******/ // The require function
7 | /******/ function __webpack_require__(moduleId) {
8 | /******/
9 | /******/ // Check if module is in cache
10 | /******/ if(installedModules[moduleId]) {
11 | /******/ return installedModules[moduleId].exports;
12 | /******/ }
13 | /******/ // Create a new module (and put it into the cache)
14 | /******/ var module = installedModules[moduleId] = {
15 | /******/ i: moduleId,
16 | /******/ l: false,
17 | /******/ exports: {}
18 | /******/ };
19 | /******/
20 | /******/ // Execute the module function
21 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
22 | /******/
23 | /******/ // Flag the module as loaded
24 | /******/ module.l = true;
25 | /******/
26 | /******/ // Return the exports of the module
27 | /******/ return module.exports;
28 | /******/ }
29 | /******/
30 | /******/
31 | /******/ // expose the modules object (__webpack_modules__)
32 | /******/ __webpack_require__.m = modules;
33 | /******/
34 | /******/ // expose the module cache
35 | /******/ __webpack_require__.c = installedModules;
36 | /******/
37 | /******/ // define getter function for harmony exports
38 | /******/ __webpack_require__.d = function(exports, name, getter) {
39 | /******/ if(!__webpack_require__.o(exports, name)) {
40 | /******/ Object.defineProperty(exports, name, {
41 | /******/ configurable: false,
42 | /******/ enumerable: true,
43 | /******/ get: getter
44 | /******/ });
45 | /******/ }
46 | /******/ };
47 | /******/
48 | /******/ // getDefaultExport function for compatibility with non-harmony modules
49 | /******/ __webpack_require__.n = function(module) {
50 | /******/ var getter = module && module.__esModule ?
51 | /******/ function getDefault() { return module['default']; } :
52 | /******/ function getModuleExports() { return module; };
53 | /******/ __webpack_require__.d(getter, 'a', getter);
54 | /******/ return getter;
55 | /******/ };
56 | /******/
57 | /******/ // Object.prototype.hasOwnProperty.call
58 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
59 | /******/
60 | /******/ // __webpack_public_path__
61 | /******/ __webpack_require__.p = "";
62 | /******/
63 | /******/ // Load entry module and return exports
64 | /******/ return __webpack_require__(__webpack_require__.s = 0);
65 | /******/ })
66 | /************************************************************************/
67 | /******/ ([
68 | /* 0 */
69 | /***/ (function(module, exports, __webpack_require__) {
70 |
71 | "use strict";
72 |
73 | function __export(m) {
74 | for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
75 | }
76 | Object.defineProperty(exports, "__esModule", { value: true });
77 | __export(__webpack_require__(1));
78 | __export(__webpack_require__(6));
79 |
80 |
81 | /***/ }),
82 | /* 1 */
83 | /***/ (function(module, exports, __webpack_require__) {
84 |
85 | "use strict";
86 |
87 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
88 | return new (P || (P = Promise))(function (resolve, reject) {
89 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
90 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
91 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
92 | step((generator = generator.apply(thisArg, _arguments || [])).next());
93 | });
94 | };
95 | var __generator = (this && this.__generator) || function (thisArg, body) {
96 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
97 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
98 | function verb(n) { return function (v) { return step([n, v]); }; }
99 | function step(op) {
100 | if (f) throw new TypeError("Generator is already executing.");
101 | while (_) try {
102 | if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
103 | if (y = 0, t) op = [0, t.value];
104 | switch (op[0]) {
105 | case 0: case 1: t = op; break;
106 | case 4: _.label++; return { value: op[1], done: false };
107 | case 5: _.label++; y = op[1]; op = [0]; continue;
108 | case 7: op = _.ops.pop(); _.trys.pop(); continue;
109 | default:
110 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
111 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
112 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
113 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
114 | if (t[2]) _.ops.pop();
115 | _.trys.pop(); continue;
116 | }
117 | op = body.call(thisArg, _);
118 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
119 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
120 | }
121 | };
122 | Object.defineProperty(exports, "__esModule", { value: true });
123 | var httpClient_1 = __webpack_require__(2);
124 | var observable_1 = __webpack_require__(5);
125 | var ClientOptions = /** @class */ (function () {
126 | function ClientOptions() {
127 | this.tokenEndpoint = "/token";
128 | this.host = window.location.origin;
129 | }
130 | return ClientOptions;
131 | }());
132 | exports.ClientOptions = ClientOptions;
133 | var ServerClient = /** @class */ (function () {
134 | function ServerClient(options) {
135 | this.options = options;
136 | this.onBeforeRequestAccessToken = new observable_1.Observable();
137 | this.onRequestAccessTokenSuccess = new observable_1.Observable();
138 | this.onBeforeRequestRefreshToken = new observable_1.Observable();
139 | this.onRequestRefreshTokenSuccess = new observable_1.Observable();
140 | this._httpClient = options.httpClient || new httpClient_1.XMLHttpRequestClient();
141 | }
142 | ServerClient.prototype.requestAccessToken = function (credentials) {
143 | return __awaiter(this, void 0, void 0, function () {
144 | var requestContent, token;
145 | return __generator(this, function (_a) {
146 | switch (_a.label) {
147 | case 0:
148 | this.onBeforeRequestAccessToken.notify(undefined);
149 | requestContent = "grant_type=password&username=" + credentials.userName + "&password=" + credentials.password;
150 | return [4 /*yield*/, this._postTokenRequest(requestContent)];
151 | case 1:
152 | token = _a.sent();
153 | this.onRequestAccessTokenSuccess.notify(token);
154 | return [2 /*return*/, token];
155 | }
156 | });
157 | });
158 | };
159 | ServerClient.prototype.refreshAccessToken = function (credentials) {
160 | return __awaiter(this, void 0, void 0, function () {
161 | var content, token;
162 | return __generator(this, function (_a) {
163 | switch (_a.label) {
164 | case 0:
165 | this.onBeforeRequestRefreshToken.notify(undefined);
166 | content = "grant_type=refresh_token&refresh_token=" + credentials.refreshToken;
167 | return [4 /*yield*/, this._postTokenRequest(content)];
168 | case 1:
169 | token = _a.sent();
170 | this.onRequestRefreshTokenSuccess.notify(token);
171 | return [2 /*return*/, token];
172 | }
173 | });
174 | });
175 | };
176 | ServerClient.prototype._postTokenRequest = function (content) {
177 | return __awaiter(this, void 0, void 0, function () {
178 | var _a, host, tokenEndpoint, response;
179 | return __generator(this, function (_b) {
180 | switch (_b.label) {
181 | case 0:
182 | _a = this.options, host = _a.host, tokenEndpoint = _a.tokenEndpoint;
183 | return [4 /*yield*/, this._httpClient.post("" + host + tokenEndpoint, {
184 | content: content
185 | })];
186 | case 1:
187 | response = _b.sent();
188 | return [2 /*return*/, this._buildTokenFromResponse(response)];
189 | }
190 | });
191 | });
192 | };
193 | ServerClient.prototype._buildTokenFromResponse = function (response) {
194 | return JSON.parse(response.content);
195 | };
196 | return ServerClient;
197 | }());
198 | exports.ServerClient = ServerClient;
199 |
200 |
201 | /***/ }),
202 | /* 2 */
203 | /***/ (function(module, exports, __webpack_require__) {
204 |
205 | "use strict";
206 |
207 | var __extends = (this && this.__extends) || (function () {
208 | var extendStatics = Object.setPrototypeOf ||
209 | ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
210 | function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
211 | return function (d, b) {
212 | extendStatics(d, b);
213 | function __() { this.constructor = d; }
214 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
215 | };
216 | })();
217 | var __assign = (this && this.__assign) || Object.assign || function(t) {
218 | for (var s, i = 1, n = arguments.length; i < n; i++) {
219 | s = arguments[i];
220 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
221 | t[p] = s[p];
222 | }
223 | return t;
224 | };
225 | Object.defineProperty(exports, "__esModule", { value: true });
226 | var httpResponse_1 = __webpack_require__(3);
227 | var httpError_1 = __webpack_require__(4);
228 | var HttpClient = /** @class */ (function () {
229 | function HttpClient() {
230 | }
231 | HttpClient.prototype.post = function (url, options) {
232 | return this.send(__assign({}, options, { method: "POST", url: url }));
233 | };
234 | return HttpClient;
235 | }());
236 | exports.HttpClient = HttpClient;
237 | var XMLHttpRequestClient = /** @class */ (function (_super) {
238 | __extends(XMLHttpRequestClient, _super);
239 | function XMLHttpRequestClient() {
240 | return _super !== null && _super.apply(this, arguments) || this;
241 | }
242 | XMLHttpRequestClient.prototype.send = function (request) {
243 | return new Promise(function (resolve, reject) {
244 | var xhr = new XMLHttpRequest();
245 | xhr.open(request.method, request.url, true);
246 | xhr.setRequestHeader("X-Request-Client", "XMLHttpClient");
247 | xhr.setRequestHeader("Content-type", request.contentType || "application/x-www-form-urlencoded");
248 | xhr.onload = function () {
249 | if (xhr.status >= 200 && xhr.status < 300) {
250 | resolve(new httpResponse_1.HttpResponse(xhr.status, xhr.statusText, xhr.response || xhr.responseText));
251 | }
252 | else {
253 | reject(new httpError_1.HttpError(xhr.statusText, xhr.status));
254 | }
255 | };
256 | xhr.onerror = function () {
257 | reject(new httpError_1.HttpError(xhr.statusText, xhr.status));
258 | };
259 | xhr.ontimeout = function () {
260 | reject(new httpError_1.HttpError("Operation timeout", 500));
261 | };
262 | xhr.send(request.content || "");
263 | });
264 | };
265 | return XMLHttpRequestClient;
266 | }(HttpClient));
267 | exports.XMLHttpRequestClient = XMLHttpRequestClient;
268 |
269 |
270 | /***/ }),
271 | /* 3 */
272 | /***/ (function(module, exports, __webpack_require__) {
273 |
274 | "use strict";
275 |
276 | Object.defineProperty(exports, "__esModule", { value: true });
277 | var HttpResponse = /** @class */ (function () {
278 | function HttpResponse(statusCode, statusText, content) {
279 | this.statusCode = statusCode;
280 | this.statusText = statusText;
281 | this.content = content;
282 | }
283 | return HttpResponse;
284 | }());
285 | exports.HttpResponse = HttpResponse;
286 |
287 |
288 | /***/ }),
289 | /* 4 */
290 | /***/ (function(module, exports, __webpack_require__) {
291 |
292 | "use strict";
293 |
294 | var __extends = (this && this.__extends) || (function () {
295 | var extendStatics = Object.setPrototypeOf ||
296 | ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
297 | function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
298 | return function (d, b) {
299 | extendStatics(d, b);
300 | function __() { this.constructor = d; }
301 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
302 | };
303 | })();
304 | Object.defineProperty(exports, "__esModule", { value: true });
305 | var HttpError = /** @class */ (function (_super) {
306 | __extends(HttpError, _super);
307 | function HttpError(errorMessage, statusCode) {
308 | var _this = _super.call(this, errorMessage) || this;
309 | _this.statusCode = statusCode;
310 | return _this;
311 | }
312 | return HttpError;
313 | }(Error));
314 | exports.HttpError = HttpError;
315 |
316 |
317 | /***/ }),
318 | /* 5 */
319 | /***/ (function(module, exports, __webpack_require__) {
320 |
321 | "use strict";
322 |
323 | Object.defineProperty(exports, "__esModule", { value: true });
324 | /**
325 | * Creates a new observer
326 | * @param callback defines the callback to call when the observer is notified
327 | * @param scope defines the current scope used to restore the JS context
328 | */
329 | var Observer = /** @class */ (function () {
330 | function Observer(callback, scope) {
331 | if (scope === void 0) { scope = null; }
332 | this.callback = callback;
333 | this.scope = scope;
334 | }
335 | return Observer;
336 | }());
337 | exports.Observer = Observer;
338 | /**
339 | * The Observable class is a simple implementation of the Observable pattern.
340 | */
341 | var Observable = /** @class */ (function () {
342 | function Observable() {
343 | this._observers = new Array();
344 | }
345 | Observable.prototype.subscribe = function (callback) {
346 | if (!callback)
347 | throw Error("You should provide a callback to subscribe to an observable");
348 | var observer = new Observer(callback);
349 | this._observers.push(observer);
350 | return observer;
351 | };
352 | Observable.prototype.notify = function (eventData) {
353 | if (!this.hasObservers())
354 | return;
355 | for (var _i = 0, _a = this._observers; _i < _a.length; _i++) {
356 | var observer = _a[_i];
357 | if (observer.scope) {
358 | observer.callback.call(observer.scope, eventData);
359 | }
360 | else {
361 | observer.callback(eventData);
362 | }
363 | }
364 | };
365 | Observable.prototype.remove = function (observer) {
366 | if (!observer)
367 | return false;
368 | var index = this._observers.indexOf(observer);
369 | if (index !== -1) {
370 | this._observers.splice(index, 1);
371 | return true;
372 | }
373 | return false;
374 | };
375 | Observable.prototype.hasObservers = function () {
376 | return this._observers.length > 0;
377 | };
378 | return Observable;
379 | }());
380 | exports.Observable = Observable;
381 |
382 |
383 | /***/ }),
384 | /* 6 */
385 | /***/ (function(module, exports, __webpack_require__) {
386 |
387 | "use strict";
388 |
389 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
390 | return new (P || (P = Promise))(function (resolve, reject) {
391 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
392 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
393 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
394 | step((generator = generator.apply(thisArg, _arguments || [])).next());
395 | });
396 | };
397 | var __generator = (this && this.__generator) || function (thisArg, body) {
398 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
399 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
400 | function verb(n) { return function (v) { return step([n, v]); }; }
401 | function step(op) {
402 | if (f) throw new TypeError("Generator is already executing.");
403 | while (_) try {
404 | if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
405 | if (y = 0, t) op = [0, t.value];
406 | switch (op[0]) {
407 | case 0: case 1: t = op; break;
408 | case 4: _.label++; return { value: op[1], done: false };
409 | case 5: _.label++; y = op[1]; op = [0]; continue;
410 | case 7: op = _.ops.pop(); _.trys.pop(); continue;
411 | default:
412 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
413 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
414 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
415 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
416 | if (t[2]) _.ops.pop();
417 | _.trys.pop(); continue;
418 | }
419 | op = body.call(thisArg, _);
420 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
421 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
422 | }
423 | };
424 | Object.defineProperty(exports, "__esModule", { value: true });
425 | var RefreshTokenServiceOptions = /** @class */ (function () {
426 | function RefreshTokenServiceOptions(intervalSeconds, refreshToken, onRefreshTokenSuccessCallback) {
427 | if (intervalSeconds === void 0) { intervalSeconds = null; }
428 | if (refreshToken === void 0) { refreshToken = ""; }
429 | this.intervalSeconds = intervalSeconds;
430 | this.refreshToken = refreshToken;
431 | this.onRefreshTokenSuccessCallback = onRefreshTokenSuccessCallback;
432 | }
433 | return RefreshTokenServiceOptions;
434 | }());
435 | exports.RefreshTokenServiceOptions = RefreshTokenServiceOptions;
436 | var RefreshTokenService = /** @class */ (function () {
437 | function RefreshTokenService(client) {
438 | this.client = client;
439 | this._aborted = false;
440 | }
441 | RefreshTokenService.prototype.start = function (refreshTokenOptions) {
442 | var _this = this;
443 | this._aborted = false;
444 | this._ensureOptions(refreshTokenOptions);
445 | this._refreshSubscription = this.client.onRequestRefreshTokenSuccess.subscribe(function (token) {
446 | refreshTokenOptions.onRefreshTokenSuccessCallback &&
447 | refreshTokenOptions.onRefreshTokenSuccessCallback(token);
448 | });
449 | this._intervalSubscription = setInterval(function () { return __awaiter(_this, void 0, void 0, function () {
450 | var token;
451 | return __generator(this, function (_a) {
452 | switch (_a.label) {
453 | case 0:
454 | if (this._aborted)
455 | return [2 /*return*/];
456 | return [4 /*yield*/, this.client.refreshAccessToken({ refreshToken: refreshTokenOptions.refreshToken })];
457 | case 1:
458 | token = _a.sent();
459 | refreshTokenOptions.refreshToken = token.refresh_token;
460 | return [2 /*return*/];
461 | }
462 | });
463 | }); }, refreshTokenOptions.intervalSeconds * 1000);
464 | };
465 | RefreshTokenService.prototype.stop = function () {
466 | this._aborted = true;
467 | if (this._intervalSubscription !== 0) {
468 | clearInterval(this._intervalSubscription);
469 | this._intervalSubscription = 0;
470 | }
471 | if (this._refreshSubscription) {
472 | this.client.onRequestRefreshTokenSuccess.remove(this._refreshSubscription);
473 | this._refreshSubscription = undefined;
474 | }
475 | };
476 | RefreshTokenService.prototype._ensureOptions = function (options) {
477 | if (!options.onRefreshTokenSuccessCallback) {
478 | throw Error("You must provide a callback to start the RefreshTokenService");
479 | }
480 | if (!options.intervalSeconds) {
481 | throw Error("You must provide the refresh token interval");
482 | }
483 | };
484 | return RefreshTokenService;
485 | }());
486 | exports.RefreshTokenService = RefreshTokenService;
487 |
488 |
489 | /***/ })
490 | /******/ ]);
491 | //# sourceMappingURL=simple-server-client.js.map
--------------------------------------------------------------------------------
/samples/SimpleServerInMemoryStorage/Controllers/TestController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Authorization;
6 | using Microsoft.AspNetCore.Mvc;
7 |
8 | // For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
9 |
10 | namespace SimpleServerInMemoryStorage.Controllers
11 | {
12 | [Route("api/[controller]")]
13 | [Authorize]
14 | public class TestController : Controller
15 | {
16 | public IActionResult Get()
17 | {
18 | return Ok("Answer from controller : Authorization successful");
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/samples/SimpleServerInMemoryStorage/CustomAuthenticationProvider.cs:
--------------------------------------------------------------------------------
1 | using JWTSimpleServer;
2 | using JWTSimpleServer.Abstractions;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Security.Claims;
7 | using System.Threading.Tasks;
8 |
9 | namespace SimpleServerInMemoryStorage
10 | {
11 | public class CustomAuthenticationProvider : IAuthenticationProvider
12 | {
13 | public Task ValidateClientAuthentication(JwtSimpleServerContext context)
14 | {
15 | if(context.UserName == "demo" && context.Password == "demo")
16 | {
17 | var claims = new List