├── .gitignore
├── .babelrc
├── .flowconfig
├── .eslintrc
├── .editorconfig
├── README.md
├── demo
└── index.html
├── package.json
├── src
└── index.js
└── dist
├── vue-keycloak.min.js
├── vue-keycloak.esm.js
├── vue-keycloak.common.js
└── vue-keycloak.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | dist/*.map
3 | .vscode/
4 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "flow-vue"],
3 | "plugins": ["syntax-dynamic-import"]
4 | }
5 |
--------------------------------------------------------------------------------
/.flowconfig:
--------------------------------------------------------------------------------
1 | [ignore]
2 | .*/node_modules/.*
3 | .*/dist/.*
4 |
5 | [include]
6 |
7 | [libs]
8 |
9 | [options]
10 | unsafe.enable_getters_and_setters=true
11 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "plugins": [
4 | "flowtype"
5 | ],
6 | "extends": [
7 | "plugin:vue-libs/recommended",
8 | "plugin:flowtype/recommended"
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | indent_style = space
8 | indent_size = 2
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vue-keycloak
2 |
3 | Use [Keycloak Javascript Adapter](https://keycloak.gitbooks.io/documentation/securing_apps/topics/oidc/javascript-adapter.html) with [Vue.js](https://vuejs.org/)
4 |
5 | ## This is a Work In Progress
6 |
7 | ## How to build
8 |
9 | ```bash
10 | git clone https://github.com/crisbal/vue-keycloak
11 | cd vue-keycloak
12 | npm install
13 | npm run build
14 | ```
15 |
16 | You will find the built files in `dist/`
17 |
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | vue-keycloak
6 |
7 |
8 |
9 |
10 |
11 |
Ready: {{ $keycloak.ready }}
12 |
Auth: {{ $keycloak.authenticated }}
13 |
14 | You are logged in
15 | User: {{ $keycloak.user }}
16 | Token: {{ $keycloak.token }}
17 | ResourceAccess: {{ $keycloak.resourceAccess }}
18 |
19 |
20 |
21 | You are not logged in
22 |
23 |
24 |
25 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-keycloak",
3 | "version": "0.0.11",
4 | "description": "Use Keycloak Javascript Adapter in Vue.js",
5 | "author": "Cristian Baldi",
6 | "license": "MIT",
7 | "main": "dist/vue-keycloak.common.js",
8 | "module": "dist/vue-keycloak.esm.js",
9 | "unpkg": "dist/vue-keycloak.js",
10 | "repository": {
11 | "type": "git",
12 | "url": "https://github.com/crisbal/vue-keycloak.git"
13 | },
14 | "bugs": {
15 | "url": "https://github.com/crisbal/vue-keycloak/issues"
16 | },
17 | "homepage": "https://github.com/crisbal/vue-keycloak",
18 | "keywords": [
19 | "vue",
20 | "keycloak"
21 | ],
22 | "scripts": {
23 | "watch": "rollup -wm -c build/dev.config.js",
24 | "build": "npm run lint && node build/build.js",
25 | "lint": "eslint src",
26 | "release": "bash build/release.sh"
27 | },
28 | "devDependencies": {
29 | "babel-core": "^6.24.1",
30 | "babel-eslint": "^7.2.3",
31 | "babel-plugin-syntax-dynamic-import": "^6.18.0",
32 | "babel-preset-es2015": "^6.24.1",
33 | "babel-preset-flow-vue": "^1.0.0",
34 | "buble": "^0.15.2",
35 | "eslint": "^3.0.1",
36 | "eslint-plugin-flowtype": "^2.34.0",
37 | "eslint-plugin-vue-libs": "^1.2.0",
38 | "flow-bin": "^0.51.0",
39 | "rollup": "^0.45.2",
40 | "rollup-plugin-buble": "^0.15.0",
41 | "rollup-plugin-commonjs": "^8.0.2",
42 | "rollup-plugin-flow-no-whitespace": "^1.0.0",
43 | "rollup-plugin-node-resolve": "^3.0.0",
44 | "rollup-plugin-replace": "^1.1.1",
45 | "rollup-watch": "^4.0.0",
46 | "uglify-js": "^3.0.17",
47 | "vue": "^2.3.0"
48 | },
49 | "dependencies": {
50 | "keycloak-js": "^3.4.0"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 | import Keycloak from 'keycloak-js'
3 |
4 | let installed: Boolean = false
5 |
6 | export default class VueKeyCloak {
7 | static install: () => void
8 | static version: string
9 | }
10 |
11 | VueKeyCloak.install = (Vue, options: Object = {
12 | keycloakOptions: {},
13 | keycloakInitOptions: {},
14 | refreshTime: 10
15 | }) => {
16 | if (installed) return
17 | installed = true
18 |
19 | const keycloak: Object = Keycloak(options.keycloakOptions)
20 |
21 | const watch = new Vue({
22 | data () {
23 | return {
24 | ready: false,
25 | authenticated: false,
26 | user: null,
27 | token: null,
28 | resourceAccess: null
29 | }
30 | }
31 | })
32 |
33 | keycloak.init(options.keycloakInitOptions).success((isAuthenticated) => {
34 | updateWatchVariables(isAuthenticated).then(() => {
35 | watch.ready = true
36 | })
37 |
38 | if (isAuthenticated) {
39 | setInterval(() => {
40 | keycloak.updateToken(options.refreshTime + 2)
41 | .success((refreshed) => {
42 | if (refreshed) updateWatchVariables(true)
43 | })
44 | }, options.refreshTime * 1000)
45 | }
46 | })
47 |
48 | function updateWatchVariables (isAuthenticated = false) {
49 | watch.authenticated = isAuthenticated
50 |
51 | if (isAuthenticated) {
52 | watch.token = keycloak.token
53 | watch.resourceAccess = keycloak.resourceAccess
54 | return new Promise((resolve, reject) => {
55 | keycloak.loadUserProfile().success((user) => {
56 | watch.user = user
57 | resolve()
58 | })
59 | })
60 | } else {
61 | return Promise.resolve()
62 | }
63 | }
64 |
65 | Object.defineProperty(Vue.prototype, '$keycloak', {
66 | get () {
67 | keycloak.ready = watch.ready
68 | keycloak.user = watch.user
69 | return keycloak
70 | }
71 | })
72 | }
73 |
74 | VueKeyCloak.version = '__VERSION__'
75 |
--------------------------------------------------------------------------------
/dist/vue-keycloak.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * vue-keycloak v0.0.11
3 | * (c) Cristian Baldi 2018
4 | */
5 | !function(e,r){"object"==typeof exports&&"undefined"!=typeof module?module.exports=r():"function"==typeof define&&define.amd?define(r):e.VueKeycloak=r()}(this,function(){"use strict";var e,s=(function(e){var U,t;U=window,t=function(i){if(!(this instanceof t))return new t(i);for(var a,u,d=this,c=[],l={enable:!0,callbackList:[],interval:5},e=document.getElementsByTagName("script"),r=0;r= 0;
324 | };
325 |
326 | kc.hasResourceRole = function(role, resource) {
327 | if (!kc.resourceAccess) {
328 | return false;
329 | }
330 |
331 | var access = kc.resourceAccess[resource || kc.clientId];
332 | return !!access && access.roles.indexOf(role) >= 0;
333 | };
334 |
335 | kc.loadUserProfile = function() {
336 | var url = getRealmUrl() + '/account';
337 | var req = new XMLHttpRequest();
338 | req.open('GET', url, true);
339 | req.setRequestHeader('Accept', 'application/json');
340 | req.setRequestHeader('Authorization', 'bearer ' + kc.token);
341 |
342 | var promise = createPromise();
343 |
344 | req.onreadystatechange = function () {
345 | if (req.readyState == 4) {
346 | if (req.status == 200) {
347 | kc.profile = JSON.parse(req.responseText);
348 | promise.setSuccess(kc.profile);
349 | } else {
350 | promise.setError();
351 | }
352 | }
353 | };
354 |
355 | req.send();
356 |
357 | return promise.promise;
358 | };
359 |
360 | kc.loadUserInfo = function() {
361 | var url = getRealmUrl() + '/protocol/openid-connect/userinfo';
362 | var req = new XMLHttpRequest();
363 | req.open('GET', url, true);
364 | req.setRequestHeader('Accept', 'application/json');
365 | req.setRequestHeader('Authorization', 'bearer ' + kc.token);
366 |
367 | var promise = createPromise();
368 |
369 | req.onreadystatechange = function () {
370 | if (req.readyState == 4) {
371 | if (req.status == 200) {
372 | kc.userInfo = JSON.parse(req.responseText);
373 | promise.setSuccess(kc.userInfo);
374 | } else {
375 | promise.setError();
376 | }
377 | }
378 | };
379 |
380 | req.send();
381 |
382 | return promise.promise;
383 | };
384 |
385 | kc.isTokenExpired = function(minValidity) {
386 | if (!kc.tokenParsed || (!kc.refreshToken && kc.flow != 'implicit' )) {
387 | throw 'Not authenticated';
388 | }
389 |
390 | if (kc.timeSkew == null) {
391 | console.info('[KEYCLOAK] Unable to determine if token is expired as timeskew is not set');
392 | return true;
393 | }
394 |
395 | var expiresIn = kc.tokenParsed['exp'] - Math.ceil(new Date().getTime() / 1000) + kc.timeSkew;
396 | if (minValidity) {
397 | expiresIn -= minValidity;
398 | }
399 | return expiresIn < 0;
400 | };
401 |
402 | kc.updateToken = function(minValidity) {
403 | var promise = createPromise();
404 |
405 | if (!kc.refreshToken) {
406 | promise.setError();
407 | return promise.promise;
408 | }
409 |
410 | minValidity = minValidity || 5;
411 |
412 | var exec = function() {
413 | var refreshToken = false;
414 | if (minValidity == -1) {
415 | refreshToken = true;
416 | console.info('[KEYCLOAK] Refreshing token: forced refresh');
417 | } else if (!kc.tokenParsed || kc.isTokenExpired(minValidity)) {
418 | refreshToken = true;
419 | console.info('[KEYCLOAK] Refreshing token: token expired');
420 | }
421 |
422 | if (!refreshToken) {
423 | promise.setSuccess(false);
424 | } else {
425 | var params = 'grant_type=refresh_token&' + 'refresh_token=' + kc.refreshToken;
426 | var url = getRealmUrl() + '/protocol/openid-connect/token';
427 |
428 | refreshQueue.push(promise);
429 |
430 | if (refreshQueue.length == 1) {
431 | var req = new XMLHttpRequest();
432 | req.open('POST', url, true);
433 | req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
434 | req.withCredentials = true;
435 |
436 | if (kc.clientId && kc.clientSecret) {
437 | req.setRequestHeader('Authorization', 'Basic ' + btoa(kc.clientId + ':' + kc.clientSecret));
438 | } else {
439 | params += '&client_id=' + encodeURIComponent(kc.clientId);
440 | }
441 |
442 | var timeLocal = new Date().getTime();
443 |
444 | req.onreadystatechange = function () {
445 | if (req.readyState == 4) {
446 | if (req.status == 200) {
447 | console.info('[KEYCLOAK] Token refreshed');
448 |
449 | timeLocal = (timeLocal + new Date().getTime()) / 2;
450 |
451 | var tokenResponse = JSON.parse(req.responseText);
452 |
453 | setToken(tokenResponse['access_token'], tokenResponse['refresh_token'], tokenResponse['id_token'], timeLocal);
454 |
455 | kc.onAuthRefreshSuccess && kc.onAuthRefreshSuccess();
456 | for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) {
457 | p.setSuccess(true);
458 | }
459 | } else {
460 | console.warn('[KEYCLOAK] Failed to refresh token');
461 |
462 | kc.onAuthRefreshError && kc.onAuthRefreshError();
463 | for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) {
464 | p.setError(true);
465 | }
466 | }
467 | }
468 | };
469 |
470 | req.send(params);
471 | }
472 | }
473 | };
474 |
475 | if (loginIframe.enable) {
476 | var iframePromise = checkLoginIframe();
477 | iframePromise.success(function() {
478 | exec();
479 | }).error(function() {
480 | promise.setError();
481 | });
482 | } else {
483 | exec();
484 | }
485 |
486 | return promise.promise;
487 | };
488 |
489 | kc.clearToken = function() {
490 | if (kc.token) {
491 | setToken(null, null, null);
492 | kc.onAuthLogout && kc.onAuthLogout();
493 | if (kc.loginRequired) {
494 | kc.login();
495 | }
496 | }
497 | };
498 |
499 | function getRealmUrl() {
500 | if (kc.authServerUrl.charAt(kc.authServerUrl.length - 1) == '/') {
501 | return kc.authServerUrl + 'realms/' + encodeURIComponent(kc.realm);
502 | } else {
503 | return kc.authServerUrl + '/realms/' + encodeURIComponent(kc.realm);
504 | }
505 | }
506 |
507 | function getOrigin() {
508 | if (!window.location.origin) {
509 | return window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: '');
510 | } else {
511 | return window.location.origin;
512 | }
513 | }
514 |
515 | function processCallback(oauth, promise) {
516 | var code = oauth.code;
517 | var error = oauth.error;
518 | var prompt = oauth.prompt;
519 |
520 | var timeLocal = new Date().getTime();
521 |
522 | if (error) {
523 | if (prompt != 'none') {
524 | var errorData = { error: error, error_description: oauth.error_description };
525 | kc.onAuthError && kc.onAuthError(errorData);
526 | promise && promise.setError(errorData);
527 | } else {
528 | promise && promise.setSuccess();
529 | }
530 | return;
531 | } else if ((kc.flow != 'standard') && (oauth.access_token || oauth.id_token)) {
532 | authSuccess(oauth.access_token, null, oauth.id_token, true);
533 | }
534 |
535 | if ((kc.flow != 'implicit') && code) {
536 | var params = 'code=' + code + '&grant_type=authorization_code';
537 | var url = getRealmUrl() + '/protocol/openid-connect/token';
538 |
539 | var req = new XMLHttpRequest();
540 | req.open('POST', url, true);
541 | req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
542 |
543 | if (kc.clientId && kc.clientSecret) {
544 | req.setRequestHeader('Authorization', 'Basic ' + btoa(kc.clientId + ':' + kc.clientSecret));
545 | } else {
546 | params += '&client_id=' + encodeURIComponent(kc.clientId);
547 | }
548 |
549 | params += '&redirect_uri=' + oauth.redirectUri;
550 |
551 | req.withCredentials = true;
552 |
553 | req.onreadystatechange = function() {
554 | if (req.readyState == 4) {
555 | if (req.status == 200) {
556 |
557 | var tokenResponse = JSON.parse(req.responseText);
558 | authSuccess(tokenResponse['access_token'], tokenResponse['refresh_token'], tokenResponse['id_token'], kc.flow === 'standard');
559 | } else {
560 | kc.onAuthError && kc.onAuthError();
561 | promise && promise.setError();
562 | }
563 | }
564 | };
565 |
566 | req.send(params);
567 | }
568 |
569 | function authSuccess(accessToken, refreshToken, idToken, fulfillPromise) {
570 | timeLocal = (timeLocal + new Date().getTime()) / 2;
571 |
572 | setToken(accessToken, refreshToken, idToken, timeLocal);
573 |
574 | if ((kc.tokenParsed && kc.tokenParsed.nonce != oauth.storedNonce) ||
575 | (kc.refreshTokenParsed && kc.refreshTokenParsed.nonce != oauth.storedNonce) ||
576 | (kc.idTokenParsed && kc.idTokenParsed.nonce != oauth.storedNonce)) {
577 |
578 | console.info('[KEYCLOAK] Invalid nonce, clearing token');
579 | kc.clearToken();
580 | promise && promise.setError();
581 | } else {
582 | if (fulfillPromise) {
583 | kc.onAuthSuccess && kc.onAuthSuccess();
584 | promise && promise.setSuccess();
585 | }
586 | }
587 | }
588 |
589 | }
590 |
591 | function loadConfig(url) {
592 | var promise = createPromise();
593 | var configUrl;
594 |
595 | if (!config) {
596 | configUrl = 'keycloak.json';
597 | } else if (typeof config === 'string') {
598 | configUrl = config;
599 | }
600 |
601 | if (configUrl) {
602 | var req = new XMLHttpRequest();
603 | req.open('GET', configUrl, true);
604 | req.setRequestHeader('Accept', 'application/json');
605 |
606 | req.onreadystatechange = function () {
607 | if (req.readyState == 4) {
608 | if (req.status == 200 || fileLoaded(req)) {
609 | var config = JSON.parse(req.responseText);
610 |
611 | kc.authServerUrl = config['auth-server-url'];
612 | kc.realm = config['realm'];
613 | kc.clientId = config['resource'];
614 | kc.clientSecret = (config['credentials'] || {})['secret'];
615 |
616 | promise.setSuccess();
617 | } else {
618 | promise.setError();
619 | }
620 | }
621 | };
622 |
623 | req.send();
624 | } else {
625 | if (!config['url']) {
626 | var scripts = document.getElementsByTagName('script');
627 | for (var i = 0; i < scripts.length; i++) {
628 | if (scripts[i].src.match(/.*keycloak\.js/)) {
629 | config.url = scripts[i].src.substr(0, scripts[i].src.indexOf('/js/keycloak.js'));
630 | break;
631 | }
632 | }
633 | }
634 |
635 | if (!config.realm) {
636 | throw 'realm missing';
637 | }
638 |
639 | if (!config.clientId) {
640 | throw 'clientId missing';
641 | }
642 |
643 | kc.authServerUrl = config.url;
644 | kc.realm = config.realm;
645 | kc.clientId = config.clientId;
646 | kc.clientSecret = (config.credentials || {}).secret;
647 |
648 | promise.setSuccess();
649 | }
650 |
651 | return promise.promise;
652 | }
653 |
654 | function fileLoaded(xhr) {
655 | return xhr.status == 0 && xhr.responseText && xhr.responseURL.startsWith('file:');
656 | }
657 |
658 | function setToken(token, refreshToken, idToken, timeLocal) {
659 | if (kc.tokenTimeoutHandle) {
660 | clearTimeout(kc.tokenTimeoutHandle);
661 | kc.tokenTimeoutHandle = null;
662 | }
663 |
664 | if (refreshToken) {
665 | kc.refreshToken = refreshToken;
666 | kc.refreshTokenParsed = decodeToken(refreshToken);
667 | } else {
668 | delete kc.refreshToken;
669 | delete kc.refreshTokenParsed;
670 | }
671 |
672 | if (idToken) {
673 | kc.idToken = idToken;
674 | kc.idTokenParsed = decodeToken(idToken);
675 | } else {
676 | delete kc.idToken;
677 | delete kc.idTokenParsed;
678 | }
679 |
680 | if (token) {
681 | kc.token = token;
682 | kc.tokenParsed = decodeToken(token);
683 | kc.sessionId = kc.tokenParsed.session_state;
684 | kc.authenticated = true;
685 | kc.subject = kc.tokenParsed.sub;
686 | kc.realmAccess = kc.tokenParsed.realm_access;
687 | kc.resourceAccess = kc.tokenParsed.resource_access;
688 |
689 | if (timeLocal) {
690 | kc.timeSkew = Math.floor(timeLocal / 1000) - kc.tokenParsed.iat;
691 | }
692 |
693 | if (kc.timeSkew != null) {
694 | console.info('[KEYCLOAK] Estimated time difference between browser and server is ' + kc.timeSkew + ' seconds');
695 |
696 | if (kc.onTokenExpired) {
697 | var expiresIn = (kc.tokenParsed['exp'] - (new Date().getTime() / 1000) + kc.timeSkew) * 1000;
698 | console.info('[KEYCLOAK] Token expires in ' + Math.round(expiresIn / 1000) + ' s');
699 | if (expiresIn <= 0) {
700 | kc.onTokenExpired();
701 | } else {
702 | kc.tokenTimeoutHandle = setTimeout(kc.onTokenExpired, expiresIn);
703 | }
704 | }
705 | }
706 | } else {
707 | delete kc.token;
708 | delete kc.tokenParsed;
709 | delete kc.subject;
710 | delete kc.realmAccess;
711 | delete kc.resourceAccess;
712 |
713 | kc.authenticated = false;
714 | }
715 | }
716 |
717 | function decodeToken(str) {
718 | str = str.split('.')[1];
719 |
720 | str = str.replace('/-/g', '+');
721 | str = str.replace('/_/g', '/');
722 | switch (str.length % 4)
723 | {
724 | case 0:
725 | break;
726 | case 2:
727 | str += '==';
728 | break;
729 | case 3:
730 | str += '=';
731 | break;
732 | default:
733 | throw 'Invalid token';
734 | }
735 |
736 | str = (str + '===').slice(0, str.length + (str.length % 4));
737 | str = str.replace(/-/g, '+').replace(/_/g, '/');
738 |
739 | str = decodeURIComponent(escape(atob(str)));
740 |
741 | str = JSON.parse(str);
742 | return str;
743 | }
744 |
745 | function createUUID() {
746 | var s = [];
747 | var hexDigits = '0123456789abcdef';
748 | for (var i = 0; i < 36; i++) {
749 | s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
750 | }
751 | s[14] = '4';
752 | s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
753 | s[8] = s[13] = s[18] = s[23] = '-';
754 | var uuid = s.join('');
755 | return uuid;
756 | }
757 |
758 | kc.callback_id = 0;
759 |
760 | function parseCallback(url) {
761 | var oauth = new CallbackParser(url, kc.responseMode).parseUri();
762 | var oauthState = callbackStorage.get(oauth.state);
763 |
764 | if (oauthState && (oauth.code || oauth.error || oauth.access_token || oauth.id_token)) {
765 | oauth.redirectUri = oauthState.redirectUri;
766 | oauth.storedNonce = oauthState.nonce;
767 | oauth.prompt = oauthState.prompt;
768 |
769 | if (oauth.fragment) {
770 | oauth.newUrl += '#' + oauth.fragment;
771 | }
772 |
773 | return oauth;
774 | }
775 | }
776 |
777 | function createPromise() {
778 | var p = {
779 | setSuccess: function(result) {
780 | p.success = true;
781 | p.result = result;
782 | if (p.successCallback) {
783 | p.successCallback(result);
784 | }
785 | },
786 |
787 | setError: function(result) {
788 | p.error = true;
789 | p.result = result;
790 | if (p.errorCallback) {
791 | p.errorCallback(result);
792 | }
793 | },
794 |
795 | promise: {
796 | success: function(callback) {
797 | if (p.success) {
798 | callback(p.result);
799 | } else if (!p.error) {
800 | p.successCallback = callback;
801 | }
802 | return p.promise;
803 | },
804 | error: function(callback) {
805 | if (p.error) {
806 | callback(p.result);
807 | } else if (!p.success) {
808 | p.errorCallback = callback;
809 | }
810 | return p.promise;
811 | }
812 | }
813 | };
814 | return p;
815 | }
816 |
817 | function setupCheckLoginIframe() {
818 | var promise = createPromise();
819 |
820 | if (!loginIframe.enable) {
821 | promise.setSuccess();
822 | return promise.promise;
823 | }
824 |
825 | if (loginIframe.iframe) {
826 | promise.setSuccess();
827 | return promise.promise;
828 | }
829 |
830 | var iframe = document.createElement('iframe');
831 | loginIframe.iframe = iframe;
832 |
833 | iframe.onload = function() {
834 | var realmUrl = getRealmUrl();
835 | if (realmUrl.charAt(0) === '/') {
836 | loginIframe.iframeOrigin = getOrigin();
837 | } else {
838 | loginIframe.iframeOrigin = realmUrl.substring(0, realmUrl.indexOf('/', 8));
839 | }
840 | promise.setSuccess();
841 |
842 | setTimeout(check, loginIframe.interval * 1000);
843 | };
844 |
845 | var src = getRealmUrl() + '/protocol/openid-connect/login-status-iframe.html';
846 | if (kc.iframeVersion) {
847 | src = src + '?version=' + kc.iframeVersion;
848 | }
849 |
850 | iframe.setAttribute('src', src );
851 | iframe.setAttribute('title', 'keycloak-session-iframe' );
852 | iframe.style.display = 'none';
853 | document.body.appendChild(iframe);
854 |
855 | var messageCallback = function(event) {
856 | if ((event.origin !== loginIframe.iframeOrigin) || (loginIframe.iframe.contentWindow !== event.source)) {
857 | return;
858 | }
859 |
860 | if (!(event.data == 'unchanged' || event.data == 'changed' || event.data == 'error')) {
861 | return;
862 | }
863 |
864 |
865 | if (event.data != 'unchanged') {
866 | kc.clearToken();
867 | }
868 |
869 | var callbacks = loginIframe.callbackList.splice(0, loginIframe.callbackList.length);
870 |
871 | for (var i = callbacks.length - 1; i >= 0; --i) {
872 | var promise = callbacks[i];
873 | if (event.data == 'unchanged') {
874 | promise.setSuccess();
875 | } else {
876 | promise.setError();
877 | }
878 | }
879 | };
880 |
881 | window.addEventListener('message', messageCallback, false);
882 |
883 | var check = function() {
884 | checkLoginIframe();
885 | if (kc.token) {
886 | setTimeout(check, loginIframe.interval * 1000);
887 | }
888 | };
889 |
890 | return promise.promise;
891 | }
892 |
893 | function checkLoginIframe() {
894 | var promise = createPromise();
895 |
896 | if (loginIframe.iframe && loginIframe.iframeOrigin ) {
897 | var msg = kc.clientId + ' ' + kc.sessionId;
898 | loginIframe.callbackList.push(promise);
899 | var origin = loginIframe.iframeOrigin;
900 | if (loginIframe.callbackList.length == 1) {
901 | loginIframe.iframe.contentWindow.postMessage(msg, origin);
902 | }
903 | } else {
904 | promise.setSuccess();
905 | }
906 |
907 | return promise.promise;
908 | }
909 |
910 | function loadAdapter(type) {
911 | if (!type || type == 'default') {
912 | return {
913 | login: function(options) {
914 | window.location.href = kc.createLoginUrl(options);
915 | return createPromise().promise;
916 | },
917 |
918 | logout: function(options) {
919 | window.location.href = kc.createLogoutUrl(options);
920 | return createPromise().promise;
921 | },
922 |
923 | register: function(options) {
924 | window.location.href = kc.createRegisterUrl(options);
925 | return createPromise().promise;
926 | },
927 |
928 | accountManagement : function() {
929 | window.location.href = kc.createAccountUrl();
930 | return createPromise().promise;
931 | },
932 |
933 | redirectUri: function(options, encodeHash) {
934 | if (arguments.length == 1) {
935 | encodeHash = true;
936 | }
937 |
938 | if (options && options.redirectUri) {
939 | return options.redirectUri;
940 | } else if (kc.redirectUri) {
941 | return kc.redirectUri;
942 | } else {
943 | var redirectUri = location.href;
944 | if (location.hash && encodeHash) {
945 | redirectUri = redirectUri.substring(0, location.href.indexOf('#'));
946 | redirectUri += (redirectUri.indexOf('?') == -1 ? '?' : '&') + 'redirect_fragment=' + encodeURIComponent(location.hash.substring(1));
947 | }
948 | return redirectUri;
949 | }
950 | }
951 | };
952 | }
953 |
954 | if (type == 'cordova') {
955 | loginIframe.enable = false;
956 | var cordovaOpenWindowWrapper = function(loginUrl, target, options) {
957 | if (window.cordova && window.cordova.InAppBrowser) {
958 | // Use inappbrowser for IOS and Android if available
959 | return window.cordova.InAppBrowser.open(loginUrl, target, options);
960 | } else {
961 | return window.open(loginUrl, target, options);
962 | }
963 | };
964 | return {
965 | login: function(options) {
966 | var promise = createPromise();
967 |
968 | var o = 'location=no';
969 | if (options && options.prompt == 'none') {
970 | o += ',hidden=yes';
971 | }
972 |
973 | var loginUrl = kc.createLoginUrl(options);
974 | var ref = cordovaOpenWindowWrapper(loginUrl, '_blank', o);
975 | var completed = false;
976 |
977 | ref.addEventListener('loadstart', function(event) {
978 | if (event.url.indexOf('http://localhost') == 0) {
979 | var callback = parseCallback(event.url);
980 | processCallback(callback, promise);
981 | ref.close();
982 | completed = true;
983 | }
984 | });
985 |
986 | ref.addEventListener('loaderror', function(event) {
987 | if (!completed) {
988 | if (event.url.indexOf('http://localhost') == 0) {
989 | var callback = parseCallback(event.url);
990 | processCallback(callback, promise);
991 | ref.close();
992 | completed = true;
993 | } else {
994 | promise.setError();
995 | ref.close();
996 | }
997 | }
998 | });
999 |
1000 | return promise.promise;
1001 | },
1002 |
1003 | logout: function(options) {
1004 | var promise = createPromise();
1005 |
1006 | var logoutUrl = kc.createLogoutUrl(options);
1007 | var ref = cordovaOpenWindowWrapper(logoutUrl, '_blank', 'location=no,hidden=yes');
1008 |
1009 | var error;
1010 |
1011 | ref.addEventListener('loadstart', function(event) {
1012 | if (event.url.indexOf('http://localhost') == 0) {
1013 | ref.close();
1014 | }
1015 | });
1016 |
1017 | ref.addEventListener('loaderror', function(event) {
1018 | if (event.url.indexOf('http://localhost') == 0) {
1019 | ref.close();
1020 | } else {
1021 | error = true;
1022 | ref.close();
1023 | }
1024 | });
1025 |
1026 | ref.addEventListener('exit', function(event) {
1027 | if (error) {
1028 | promise.setError();
1029 | } else {
1030 | kc.clearToken();
1031 | promise.setSuccess();
1032 | }
1033 | });
1034 |
1035 | return promise.promise;
1036 | },
1037 |
1038 | register : function() {
1039 | var registerUrl = kc.createRegisterUrl();
1040 | var ref = cordovaOpenWindowWrapper(registerUrl, '_blank', 'location=no');
1041 | ref.addEventListener('loadstart', function(event) {
1042 | if (event.url.indexOf('http://localhost') == 0) {
1043 | ref.close();
1044 | }
1045 | });
1046 | },
1047 |
1048 | accountManagement : function() {
1049 | var accountUrl = kc.createAccountUrl();
1050 | var ref = cordovaOpenWindowWrapper(accountUrl, '_blank', 'location=no');
1051 | ref.addEventListener('loadstart', function(event) {
1052 | if (event.url.indexOf('http://localhost') == 0) {
1053 | ref.close();
1054 | }
1055 | });
1056 | },
1057 |
1058 | redirectUri: function(options) {
1059 | return 'http://localhost';
1060 | }
1061 | }
1062 | }
1063 |
1064 | throw 'invalid adapter type: ' + type;
1065 | }
1066 |
1067 | var LocalStorage = function() {
1068 | if (!(this instanceof LocalStorage)) {
1069 | return new LocalStorage();
1070 | }
1071 |
1072 | localStorage.setItem('kc-test', 'test');
1073 | localStorage.removeItem('kc-test');
1074 |
1075 | var cs = this;
1076 |
1077 | function clearExpired() {
1078 | var time = new Date().getTime();
1079 | for (var i = 0; i < localStorage.length; i++) {
1080 | var key = localStorage.key(i);
1081 | if (key && key.indexOf('kc-callback-') == 0) {
1082 | var value = localStorage.getItem(key);
1083 | if (value) {
1084 | try {
1085 | var expires = JSON.parse(value).expires;
1086 | if (!expires || expires < time) {
1087 | localStorage.removeItem(key);
1088 | }
1089 | } catch (err) {
1090 | localStorage.removeItem(key);
1091 | }
1092 | }
1093 | }
1094 | }
1095 | }
1096 |
1097 | cs.get = function(state) {
1098 | if (!state) {
1099 | return;
1100 | }
1101 |
1102 | var key = 'kc-callback-' + state;
1103 | var value = localStorage.getItem(key);
1104 | if (value) {
1105 | localStorage.removeItem(key);
1106 | value = JSON.parse(value);
1107 | }
1108 |
1109 | clearExpired();
1110 | return value;
1111 | };
1112 |
1113 | cs.add = function(state) {
1114 | clearExpired();
1115 |
1116 | var key = 'kc-callback-' + state.state;
1117 | state.expires = new Date().getTime() + (60 * 60 * 1000);
1118 | localStorage.setItem(key, JSON.stringify(state));
1119 | };
1120 | };
1121 |
1122 | var CookieStorage = function() {
1123 | if (!(this instanceof CookieStorage)) {
1124 | return new CookieStorage();
1125 | }
1126 |
1127 | var cs = this;
1128 |
1129 | cs.get = function(state) {
1130 | if (!state) {
1131 | return;
1132 | }
1133 |
1134 | var value = getCookie('kc-callback-' + state);
1135 | setCookie('kc-callback-' + state, '', cookieExpiration(-100));
1136 | if (value) {
1137 | return JSON.parse(value);
1138 | }
1139 | };
1140 |
1141 | cs.add = function(state) {
1142 | setCookie('kc-callback-' + state.state, JSON.stringify(state), cookieExpiration(60));
1143 | };
1144 |
1145 | cs.removeItem = function(key) {
1146 | setCookie(key, '', cookieExpiration(-100));
1147 | };
1148 |
1149 | var cookieExpiration = function (minutes) {
1150 | var exp = new Date();
1151 | exp.setTime(exp.getTime() + (minutes*60*1000));
1152 | return exp;
1153 | };
1154 |
1155 | var getCookie = function (key) {
1156 | var name = key + '=';
1157 | var ca = document.cookie.split(';');
1158 | for (var i = 0; i < ca.length; i++) {
1159 | var c = ca[i];
1160 | while (c.charAt(0) == ' ') {
1161 | c = c.substring(1);
1162 | }
1163 | if (c.indexOf(name) == 0) {
1164 | return c.substring(name.length, c.length);
1165 | }
1166 | }
1167 | return '';
1168 | };
1169 |
1170 | var setCookie = function (key, value, expirationDate) {
1171 | var cookie = key + '=' + value + '; '
1172 | + 'expires=' + expirationDate.toUTCString() + '; ';
1173 | document.cookie = cookie;
1174 | };
1175 | };
1176 |
1177 | function createCallbackStorage() {
1178 | try {
1179 | return new LocalStorage();
1180 | } catch (err) {
1181 | }
1182 |
1183 | return new CookieStorage();
1184 | }
1185 |
1186 | var CallbackParser = function(uriToParse, responseMode) {
1187 | if (!(this instanceof CallbackParser)) {
1188 | return new CallbackParser(uriToParse, responseMode);
1189 | }
1190 | var parser = this;
1191 |
1192 | var initialParse = function() {
1193 | var baseUri = null;
1194 | var queryString = null;
1195 | var fragmentString = null;
1196 |
1197 | var questionMarkIndex = uriToParse.indexOf("?");
1198 | var fragmentIndex = uriToParse.indexOf("#", questionMarkIndex + 1);
1199 | if (questionMarkIndex == -1 && fragmentIndex == -1) {
1200 | baseUri = uriToParse;
1201 | } else if (questionMarkIndex != -1) {
1202 | baseUri = uriToParse.substring(0, questionMarkIndex);
1203 | queryString = uriToParse.substring(questionMarkIndex + 1);
1204 | if (fragmentIndex != -1) {
1205 | fragmentIndex = queryString.indexOf("#");
1206 | fragmentString = queryString.substring(fragmentIndex + 1);
1207 | queryString = queryString.substring(0, fragmentIndex);
1208 | }
1209 | } else {
1210 | baseUri = uriToParse.substring(0, fragmentIndex);
1211 | fragmentString = uriToParse.substring(fragmentIndex + 1);
1212 | }
1213 |
1214 | return { baseUri: baseUri, queryString: queryString, fragmentString: fragmentString };
1215 | };
1216 |
1217 | var parseParams = function(paramString) {
1218 | var result = {};
1219 | var params = paramString.split('&');
1220 | for (var i = 0; i < params.length; i++) {
1221 | var p = params[i].split('=');
1222 | var paramName = decodeURIComponent(p[0]);
1223 | var paramValue = decodeURIComponent(p[1]);
1224 | result[paramName] = paramValue;
1225 | }
1226 | return result;
1227 | };
1228 |
1229 | var handleQueryParam = function(paramName, paramValue, oauth) {
1230 | var supportedOAuthParams = [ 'code', 'state', 'error', 'error_description' ];
1231 |
1232 | for (var i = 0 ; i< supportedOAuthParams.length ; i++) {
1233 | if (paramName === supportedOAuthParams[i]) {
1234 | oauth[paramName] = paramValue;
1235 | return true;
1236 | }
1237 | }
1238 | return false;
1239 | };
1240 |
1241 |
1242 | parser.parseUri = function() {
1243 | var parsedUri = initialParse();
1244 |
1245 | var queryParams = {};
1246 | if (parsedUri.queryString) {
1247 | queryParams = parseParams(parsedUri.queryString);
1248 | }
1249 |
1250 | var oauth = { newUrl: parsedUri.baseUri };
1251 | for (var param in queryParams) {
1252 | switch (param) {
1253 | case 'redirect_fragment':
1254 | oauth.fragment = queryParams[param];
1255 | break;
1256 | default:
1257 | if (responseMode != 'query' || !handleQueryParam(param, queryParams[param], oauth)) {
1258 | oauth.newUrl += (oauth.newUrl.indexOf('?') == -1 ? '?' : '&') + param + '=' + encodeURIComponent(queryParams[param]);
1259 | }
1260 | break;
1261 | }
1262 | }
1263 |
1264 | if (responseMode === 'fragment') {
1265 | var fragmentParams = {};
1266 | if (parsedUri.fragmentString) {
1267 | fragmentParams = parseParams(parsedUri.fragmentString);
1268 | }
1269 | for (var param in fragmentParams) {
1270 | oauth[param] = fragmentParams[param];
1271 | }
1272 | }
1273 |
1274 | return oauth;
1275 | };
1276 | };
1277 |
1278 | };
1279 |
1280 | if ( 'object' === "object" && module && 'object' === "object" ) {
1281 | module.exports = Keycloak;
1282 | } else {
1283 | window.Keycloak = Keycloak;
1284 |
1285 | if ( typeof undefined === "function" && undefined.amd ) {
1286 | undefined( "keycloak", [], function () { return Keycloak; } );
1287 | }
1288 | }
1289 | })( window );
1290 | });
1291 |
1292 | /* */
1293 | var installed = false;
1294 |
1295 | var VueKeyCloak = function VueKeyCloak () {};
1296 |
1297 | VueKeyCloak.install = function (Vue, options) {
1298 | if ( options === void 0 ) options = {
1299 | keycloakOptions: {},
1300 | keycloakInitOptions: {},
1301 | refreshTime: 10
1302 | };
1303 |
1304 | if (installed) { return }
1305 | installed = true;
1306 |
1307 | var keycloak$$1 = keycloak(options.keycloakOptions);
1308 |
1309 | var watch = new Vue({
1310 | data: function data () {
1311 | return {
1312 | ready: false,
1313 | authenticated: false,
1314 | user: null,
1315 | token: null,
1316 | resourceAccess: null
1317 | }
1318 | }
1319 | });
1320 |
1321 | keycloak$$1.init(options.keycloakInitOptions).success(function (isAuthenticated) {
1322 | updateWatchVariables(isAuthenticated).then(function () {
1323 | watch.ready = true;
1324 | });
1325 |
1326 | if (isAuthenticated) {
1327 | setInterval(function () {
1328 | keycloak$$1.updateToken(options.refreshTime + 2)
1329 | .success(function (refreshed) {
1330 | if (refreshed) { updateWatchVariables(true); }
1331 | });
1332 | }, options.refreshTime * 1000);
1333 | }
1334 | });
1335 |
1336 | function updateWatchVariables (isAuthenticated) {
1337 | if ( isAuthenticated === void 0 ) isAuthenticated = false;
1338 |
1339 | watch.authenticated = isAuthenticated;
1340 |
1341 | if (isAuthenticated) {
1342 | watch.token = keycloak$$1.token;
1343 | watch.resourceAccess = keycloak$$1.resourceAccess;
1344 | return new Promise(function (resolve, reject) {
1345 | keycloak$$1.loadUserProfile().success(function (user) {
1346 | watch.user = user;
1347 | resolve();
1348 | });
1349 | })
1350 | } else {
1351 | return Promise.resolve()
1352 | }
1353 | }
1354 |
1355 | Object.defineProperty(Vue.prototype, '$keycloak', {
1356 | get: function get () {
1357 | keycloak$$1.ready = watch.ready;
1358 | keycloak$$1.user = watch.user;
1359 | return keycloak$$1
1360 | }
1361 | });
1362 | };
1363 |
1364 | VueKeyCloak.version = '0.0.11';
1365 |
1366 | export default VueKeyCloak;
1367 |
--------------------------------------------------------------------------------
/dist/vue-keycloak.common.js:
--------------------------------------------------------------------------------
1 | /**
2 | * vue-keycloak v0.0.11
3 | * (c) Cristian Baldi 2018
4 | */
5 | 'use strict';
6 |
7 | function createCommonjsModule(fn, module) {
8 | return module = { exports: {} }, fn(module, module.exports), module.exports;
9 | }
10 |
11 | var keycloak = createCommonjsModule(function (module) {
12 | /*
13 | * Copyright 2016 Red Hat, Inc. and/or its affiliates
14 | * and other contributors as indicated by the @author tags.
15 | *
16 | * Licensed under the Apache License, Version 2.0 (the "License");
17 | * you may not use this file except in compliance with the License.
18 | * You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing, software
23 | * distributed under the License is distributed on an "AS IS" BASIS,
24 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25 | * See the License for the specific language governing permissions and
26 | * limitations under the License.
27 | */
28 |
29 | (function( window, undefined ) {
30 |
31 | var Keycloak = function (config) {
32 | if (!(this instanceof Keycloak)) {
33 | return new Keycloak(config);
34 | }
35 |
36 | var kc = this;
37 | var adapter;
38 | var refreshQueue = [];
39 | var callbackStorage;
40 |
41 | var loginIframe = {
42 | enable: true,
43 | callbackList: [],
44 | interval: 5
45 | };
46 |
47 | var scripts = document.getElementsByTagName('script');
48 | for (var i = 0; i < scripts.length; i++) {
49 | if ((scripts[i].src.indexOf('keycloak.js') !== -1 || scripts[i].src.indexOf('keycloak.min.js') !== -1) && scripts[i].src.indexOf('version=') !== -1) {
50 | kc.iframeVersion = scripts[i].src.substring(scripts[i].src.indexOf('version=') + 8).split('&')[0];
51 | }
52 | }
53 |
54 | kc.init = function (initOptions) {
55 | kc.authenticated = false;
56 |
57 | callbackStorage = createCallbackStorage();
58 |
59 | if (initOptions && initOptions.adapter === 'cordova') {
60 | adapter = loadAdapter('cordova');
61 | } else if (initOptions && initOptions.adapter === 'default') {
62 | adapter = loadAdapter();
63 | } else {
64 | if (window.Cordova || window.cordova) {
65 | adapter = loadAdapter('cordova');
66 | } else {
67 | adapter = loadAdapter();
68 | }
69 | }
70 |
71 | if (initOptions) {
72 | if (typeof initOptions.checkLoginIframe !== 'undefined') {
73 | loginIframe.enable = initOptions.checkLoginIframe;
74 | }
75 |
76 | if (initOptions.checkLoginIframeInterval) {
77 | loginIframe.interval = initOptions.checkLoginIframeInterval;
78 | }
79 |
80 | if (initOptions.onLoad === 'login-required') {
81 | kc.loginRequired = true;
82 | }
83 |
84 | if (initOptions.responseMode) {
85 | if (initOptions.responseMode === 'query' || initOptions.responseMode === 'fragment') {
86 | kc.responseMode = initOptions.responseMode;
87 | } else {
88 | throw 'Invalid value for responseMode';
89 | }
90 | }
91 |
92 | if (initOptions.flow) {
93 | switch (initOptions.flow) {
94 | case 'standard':
95 | kc.responseType = 'code';
96 | break;
97 | case 'implicit':
98 | kc.responseType = 'id_token token';
99 | break;
100 | case 'hybrid':
101 | kc.responseType = 'code id_token token';
102 | break;
103 | default:
104 | throw 'Invalid value for flow';
105 | }
106 | kc.flow = initOptions.flow;
107 | }
108 |
109 | if (initOptions.timeSkew != null) {
110 | kc.timeSkew = initOptions.timeSkew;
111 | }
112 | }
113 |
114 | if (!kc.responseMode) {
115 | kc.responseMode = 'fragment';
116 | }
117 | if (!kc.responseType) {
118 | kc.responseType = 'code';
119 | kc.flow = 'standard';
120 | }
121 |
122 | var promise = createPromise();
123 |
124 | var initPromise = createPromise();
125 | initPromise.promise.success(function() {
126 | kc.onReady && kc.onReady(kc.authenticated);
127 | promise.setSuccess(kc.authenticated);
128 | }).error(function(errorData) {
129 | promise.setError(errorData);
130 | });
131 |
132 | var configPromise = loadConfig(config);
133 |
134 | function onLoad() {
135 | var doLogin = function(prompt) {
136 | if (!prompt) {
137 | options.prompt = 'none';
138 | }
139 | kc.login(options).success(function () {
140 | initPromise.setSuccess();
141 | }).error(function () {
142 | initPromise.setError();
143 | });
144 | };
145 |
146 | var options = {};
147 | switch (initOptions.onLoad) {
148 | case 'check-sso':
149 | if (loginIframe.enable) {
150 | setupCheckLoginIframe().success(function() {
151 | checkLoginIframe().success(function () {
152 | doLogin(false);
153 | }).error(function () {
154 | initPromise.setSuccess();
155 | });
156 | });
157 | } else {
158 | doLogin(false);
159 | }
160 | break;
161 | case 'login-required':
162 | doLogin(true);
163 | break;
164 | default:
165 | throw 'Invalid value for onLoad';
166 | }
167 | }
168 |
169 | function processInit() {
170 | var callback = parseCallback(window.location.href);
171 |
172 | if (callback) {
173 | return setupCheckLoginIframe().success(function() {
174 | window.history.replaceState({}, null, callback.newUrl);
175 | processCallback(callback, initPromise);
176 | }).error(function (e) {
177 | initPromise.setError();
178 | });
179 | } else if (initOptions) {
180 | if (initOptions.token && initOptions.refreshToken) {
181 | setToken(initOptions.token, initOptions.refreshToken, initOptions.idToken);
182 |
183 | if (loginIframe.enable) {
184 | setupCheckLoginIframe().success(function() {
185 | checkLoginIframe().success(function () {
186 | kc.onAuthSuccess && kc.onAuthSuccess();
187 | initPromise.setSuccess();
188 | }).error(function () {
189 | setToken(null, null, null);
190 | initPromise.setSuccess();
191 | });
192 | });
193 | } else {
194 | kc.updateToken(-1).success(function() {
195 | kc.onAuthSuccess && kc.onAuthSuccess();
196 | initPromise.setSuccess();
197 | }).error(function() {
198 | kc.onAuthError && kc.onAuthError();
199 | if (initOptions.onLoad) {
200 | onLoad();
201 | } else {
202 | initPromise.setError();
203 | }
204 | });
205 | }
206 | } else if (initOptions.onLoad) {
207 | onLoad();
208 | } else {
209 | initPromise.setSuccess();
210 | }
211 | } else {
212 | initPromise.setSuccess();
213 | }
214 | }
215 |
216 | configPromise.success(processInit);
217 | configPromise.error(function() {
218 | promise.setError();
219 | });
220 |
221 | return promise.promise;
222 | };
223 |
224 | kc.login = function (options) {
225 | return adapter.login(options);
226 | };
227 |
228 | kc.createLoginUrl = function(options) {
229 | var state = createUUID();
230 | var nonce = createUUID();
231 |
232 | var redirectUri = adapter.redirectUri(options);
233 |
234 | var callbackState = {
235 | state: state,
236 | nonce: nonce,
237 | redirectUri: encodeURIComponent(redirectUri)
238 | };
239 |
240 | if (options && options.prompt) {
241 | callbackState.prompt = options.prompt;
242 | }
243 |
244 | callbackStorage.add(callbackState);
245 |
246 | var action = 'auth';
247 | if (options && options.action == 'register') {
248 | action = 'registrations';
249 | }
250 |
251 | var scope = (options && options.scope) ? "openid " + options.scope : "openid";
252 |
253 | var url = getRealmUrl()
254 | + '/protocol/openid-connect/' + action
255 | + '?client_id=' + encodeURIComponent(kc.clientId)
256 | + '&redirect_uri=' + encodeURIComponent(redirectUri)
257 | + '&state=' + encodeURIComponent(state)
258 | + '&nonce=' + encodeURIComponent(nonce)
259 | + '&response_mode=' + encodeURIComponent(kc.responseMode)
260 | + '&response_type=' + encodeURIComponent(kc.responseType)
261 | + '&scope=' + encodeURIComponent(scope);
262 |
263 | if (options && options.prompt) {
264 | url += '&prompt=' + encodeURIComponent(options.prompt);
265 | }
266 |
267 | if (options && options.maxAge) {
268 | url += '&max_age=' + encodeURIComponent(options.maxAge);
269 | }
270 |
271 | if (options && options.loginHint) {
272 | url += '&login_hint=' + encodeURIComponent(options.loginHint);
273 | }
274 |
275 | if (options && options.idpHint) {
276 | url += '&kc_idp_hint=' + encodeURIComponent(options.idpHint);
277 | }
278 |
279 | if (options && options.locale) {
280 | url += '&ui_locales=' + encodeURIComponent(options.locale);
281 | }
282 |
283 | return url;
284 | };
285 |
286 | kc.logout = function(options) {
287 | return adapter.logout(options);
288 | };
289 |
290 | kc.createLogoutUrl = function(options) {
291 | var url = getRealmUrl()
292 | + '/protocol/openid-connect/logout'
293 | + '?redirect_uri=' + encodeURIComponent(adapter.redirectUri(options, false));
294 |
295 | return url;
296 | };
297 |
298 | kc.register = function (options) {
299 | return adapter.register(options);
300 | };
301 |
302 | kc.createRegisterUrl = function(options) {
303 | if (!options) {
304 | options = {};
305 | }
306 | options.action = 'register';
307 | return kc.createLoginUrl(options);
308 | };
309 |
310 | kc.createAccountUrl = function(options) {
311 | var url = getRealmUrl()
312 | + '/account'
313 | + '?referrer=' + encodeURIComponent(kc.clientId)
314 | + '&referrer_uri=' + encodeURIComponent(adapter.redirectUri(options));
315 |
316 | return url;
317 | };
318 |
319 | kc.accountManagement = function() {
320 | return adapter.accountManagement();
321 | };
322 |
323 | kc.hasRealmRole = function (role) {
324 | var access = kc.realmAccess;
325 | return !!access && access.roles.indexOf(role) >= 0;
326 | };
327 |
328 | kc.hasResourceRole = function(role, resource) {
329 | if (!kc.resourceAccess) {
330 | return false;
331 | }
332 |
333 | var access = kc.resourceAccess[resource || kc.clientId];
334 | return !!access && access.roles.indexOf(role) >= 0;
335 | };
336 |
337 | kc.loadUserProfile = function() {
338 | var url = getRealmUrl() + '/account';
339 | var req = new XMLHttpRequest();
340 | req.open('GET', url, true);
341 | req.setRequestHeader('Accept', 'application/json');
342 | req.setRequestHeader('Authorization', 'bearer ' + kc.token);
343 |
344 | var promise = createPromise();
345 |
346 | req.onreadystatechange = function () {
347 | if (req.readyState == 4) {
348 | if (req.status == 200) {
349 | kc.profile = JSON.parse(req.responseText);
350 | promise.setSuccess(kc.profile);
351 | } else {
352 | promise.setError();
353 | }
354 | }
355 | };
356 |
357 | req.send();
358 |
359 | return promise.promise;
360 | };
361 |
362 | kc.loadUserInfo = function() {
363 | var url = getRealmUrl() + '/protocol/openid-connect/userinfo';
364 | var req = new XMLHttpRequest();
365 | req.open('GET', url, true);
366 | req.setRequestHeader('Accept', 'application/json');
367 | req.setRequestHeader('Authorization', 'bearer ' + kc.token);
368 |
369 | var promise = createPromise();
370 |
371 | req.onreadystatechange = function () {
372 | if (req.readyState == 4) {
373 | if (req.status == 200) {
374 | kc.userInfo = JSON.parse(req.responseText);
375 | promise.setSuccess(kc.userInfo);
376 | } else {
377 | promise.setError();
378 | }
379 | }
380 | };
381 |
382 | req.send();
383 |
384 | return promise.promise;
385 | };
386 |
387 | kc.isTokenExpired = function(minValidity) {
388 | if (!kc.tokenParsed || (!kc.refreshToken && kc.flow != 'implicit' )) {
389 | throw 'Not authenticated';
390 | }
391 |
392 | if (kc.timeSkew == null) {
393 | console.info('[KEYCLOAK] Unable to determine if token is expired as timeskew is not set');
394 | return true;
395 | }
396 |
397 | var expiresIn = kc.tokenParsed['exp'] - Math.ceil(new Date().getTime() / 1000) + kc.timeSkew;
398 | if (minValidity) {
399 | expiresIn -= minValidity;
400 | }
401 | return expiresIn < 0;
402 | };
403 |
404 | kc.updateToken = function(minValidity) {
405 | var promise = createPromise();
406 |
407 | if (!kc.refreshToken) {
408 | promise.setError();
409 | return promise.promise;
410 | }
411 |
412 | minValidity = minValidity || 5;
413 |
414 | var exec = function() {
415 | var refreshToken = false;
416 | if (minValidity == -1) {
417 | refreshToken = true;
418 | console.info('[KEYCLOAK] Refreshing token: forced refresh');
419 | } else if (!kc.tokenParsed || kc.isTokenExpired(minValidity)) {
420 | refreshToken = true;
421 | console.info('[KEYCLOAK] Refreshing token: token expired');
422 | }
423 |
424 | if (!refreshToken) {
425 | promise.setSuccess(false);
426 | } else {
427 | var params = 'grant_type=refresh_token&' + 'refresh_token=' + kc.refreshToken;
428 | var url = getRealmUrl() + '/protocol/openid-connect/token';
429 |
430 | refreshQueue.push(promise);
431 |
432 | if (refreshQueue.length == 1) {
433 | var req = new XMLHttpRequest();
434 | req.open('POST', url, true);
435 | req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
436 | req.withCredentials = true;
437 |
438 | if (kc.clientId && kc.clientSecret) {
439 | req.setRequestHeader('Authorization', 'Basic ' + btoa(kc.clientId + ':' + kc.clientSecret));
440 | } else {
441 | params += '&client_id=' + encodeURIComponent(kc.clientId);
442 | }
443 |
444 | var timeLocal = new Date().getTime();
445 |
446 | req.onreadystatechange = function () {
447 | if (req.readyState == 4) {
448 | if (req.status == 200) {
449 | console.info('[KEYCLOAK] Token refreshed');
450 |
451 | timeLocal = (timeLocal + new Date().getTime()) / 2;
452 |
453 | var tokenResponse = JSON.parse(req.responseText);
454 |
455 | setToken(tokenResponse['access_token'], tokenResponse['refresh_token'], tokenResponse['id_token'], timeLocal);
456 |
457 | kc.onAuthRefreshSuccess && kc.onAuthRefreshSuccess();
458 | for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) {
459 | p.setSuccess(true);
460 | }
461 | } else {
462 | console.warn('[KEYCLOAK] Failed to refresh token');
463 |
464 | kc.onAuthRefreshError && kc.onAuthRefreshError();
465 | for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) {
466 | p.setError(true);
467 | }
468 | }
469 | }
470 | };
471 |
472 | req.send(params);
473 | }
474 | }
475 | };
476 |
477 | if (loginIframe.enable) {
478 | var iframePromise = checkLoginIframe();
479 | iframePromise.success(function() {
480 | exec();
481 | }).error(function() {
482 | promise.setError();
483 | });
484 | } else {
485 | exec();
486 | }
487 |
488 | return promise.promise;
489 | };
490 |
491 | kc.clearToken = function() {
492 | if (kc.token) {
493 | setToken(null, null, null);
494 | kc.onAuthLogout && kc.onAuthLogout();
495 | if (kc.loginRequired) {
496 | kc.login();
497 | }
498 | }
499 | };
500 |
501 | function getRealmUrl() {
502 | if (kc.authServerUrl.charAt(kc.authServerUrl.length - 1) == '/') {
503 | return kc.authServerUrl + 'realms/' + encodeURIComponent(kc.realm);
504 | } else {
505 | return kc.authServerUrl + '/realms/' + encodeURIComponent(kc.realm);
506 | }
507 | }
508 |
509 | function getOrigin() {
510 | if (!window.location.origin) {
511 | return window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: '');
512 | } else {
513 | return window.location.origin;
514 | }
515 | }
516 |
517 | function processCallback(oauth, promise) {
518 | var code = oauth.code;
519 | var error = oauth.error;
520 | var prompt = oauth.prompt;
521 |
522 | var timeLocal = new Date().getTime();
523 |
524 | if (error) {
525 | if (prompt != 'none') {
526 | var errorData = { error: error, error_description: oauth.error_description };
527 | kc.onAuthError && kc.onAuthError(errorData);
528 | promise && promise.setError(errorData);
529 | } else {
530 | promise && promise.setSuccess();
531 | }
532 | return;
533 | } else if ((kc.flow != 'standard') && (oauth.access_token || oauth.id_token)) {
534 | authSuccess(oauth.access_token, null, oauth.id_token, true);
535 | }
536 |
537 | if ((kc.flow != 'implicit') && code) {
538 | var params = 'code=' + code + '&grant_type=authorization_code';
539 | var url = getRealmUrl() + '/protocol/openid-connect/token';
540 |
541 | var req = new XMLHttpRequest();
542 | req.open('POST', url, true);
543 | req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
544 |
545 | if (kc.clientId && kc.clientSecret) {
546 | req.setRequestHeader('Authorization', 'Basic ' + btoa(kc.clientId + ':' + kc.clientSecret));
547 | } else {
548 | params += '&client_id=' + encodeURIComponent(kc.clientId);
549 | }
550 |
551 | params += '&redirect_uri=' + oauth.redirectUri;
552 |
553 | req.withCredentials = true;
554 |
555 | req.onreadystatechange = function() {
556 | if (req.readyState == 4) {
557 | if (req.status == 200) {
558 |
559 | var tokenResponse = JSON.parse(req.responseText);
560 | authSuccess(tokenResponse['access_token'], tokenResponse['refresh_token'], tokenResponse['id_token'], kc.flow === 'standard');
561 | } else {
562 | kc.onAuthError && kc.onAuthError();
563 | promise && promise.setError();
564 | }
565 | }
566 | };
567 |
568 | req.send(params);
569 | }
570 |
571 | function authSuccess(accessToken, refreshToken, idToken, fulfillPromise) {
572 | timeLocal = (timeLocal + new Date().getTime()) / 2;
573 |
574 | setToken(accessToken, refreshToken, idToken, timeLocal);
575 |
576 | if ((kc.tokenParsed && kc.tokenParsed.nonce != oauth.storedNonce) ||
577 | (kc.refreshTokenParsed && kc.refreshTokenParsed.nonce != oauth.storedNonce) ||
578 | (kc.idTokenParsed && kc.idTokenParsed.nonce != oauth.storedNonce)) {
579 |
580 | console.info('[KEYCLOAK] Invalid nonce, clearing token');
581 | kc.clearToken();
582 | promise && promise.setError();
583 | } else {
584 | if (fulfillPromise) {
585 | kc.onAuthSuccess && kc.onAuthSuccess();
586 | promise && promise.setSuccess();
587 | }
588 | }
589 | }
590 |
591 | }
592 |
593 | function loadConfig(url) {
594 | var promise = createPromise();
595 | var configUrl;
596 |
597 | if (!config) {
598 | configUrl = 'keycloak.json';
599 | } else if (typeof config === 'string') {
600 | configUrl = config;
601 | }
602 |
603 | if (configUrl) {
604 | var req = new XMLHttpRequest();
605 | req.open('GET', configUrl, true);
606 | req.setRequestHeader('Accept', 'application/json');
607 |
608 | req.onreadystatechange = function () {
609 | if (req.readyState == 4) {
610 | if (req.status == 200 || fileLoaded(req)) {
611 | var config = JSON.parse(req.responseText);
612 |
613 | kc.authServerUrl = config['auth-server-url'];
614 | kc.realm = config['realm'];
615 | kc.clientId = config['resource'];
616 | kc.clientSecret = (config['credentials'] || {})['secret'];
617 |
618 | promise.setSuccess();
619 | } else {
620 | promise.setError();
621 | }
622 | }
623 | };
624 |
625 | req.send();
626 | } else {
627 | if (!config['url']) {
628 | var scripts = document.getElementsByTagName('script');
629 | for (var i = 0; i < scripts.length; i++) {
630 | if (scripts[i].src.match(/.*keycloak\.js/)) {
631 | config.url = scripts[i].src.substr(0, scripts[i].src.indexOf('/js/keycloak.js'));
632 | break;
633 | }
634 | }
635 | }
636 |
637 | if (!config.realm) {
638 | throw 'realm missing';
639 | }
640 |
641 | if (!config.clientId) {
642 | throw 'clientId missing';
643 | }
644 |
645 | kc.authServerUrl = config.url;
646 | kc.realm = config.realm;
647 | kc.clientId = config.clientId;
648 | kc.clientSecret = (config.credentials || {}).secret;
649 |
650 | promise.setSuccess();
651 | }
652 |
653 | return promise.promise;
654 | }
655 |
656 | function fileLoaded(xhr) {
657 | return xhr.status == 0 && xhr.responseText && xhr.responseURL.startsWith('file:');
658 | }
659 |
660 | function setToken(token, refreshToken, idToken, timeLocal) {
661 | if (kc.tokenTimeoutHandle) {
662 | clearTimeout(kc.tokenTimeoutHandle);
663 | kc.tokenTimeoutHandle = null;
664 | }
665 |
666 | if (refreshToken) {
667 | kc.refreshToken = refreshToken;
668 | kc.refreshTokenParsed = decodeToken(refreshToken);
669 | } else {
670 | delete kc.refreshToken;
671 | delete kc.refreshTokenParsed;
672 | }
673 |
674 | if (idToken) {
675 | kc.idToken = idToken;
676 | kc.idTokenParsed = decodeToken(idToken);
677 | } else {
678 | delete kc.idToken;
679 | delete kc.idTokenParsed;
680 | }
681 |
682 | if (token) {
683 | kc.token = token;
684 | kc.tokenParsed = decodeToken(token);
685 | kc.sessionId = kc.tokenParsed.session_state;
686 | kc.authenticated = true;
687 | kc.subject = kc.tokenParsed.sub;
688 | kc.realmAccess = kc.tokenParsed.realm_access;
689 | kc.resourceAccess = kc.tokenParsed.resource_access;
690 |
691 | if (timeLocal) {
692 | kc.timeSkew = Math.floor(timeLocal / 1000) - kc.tokenParsed.iat;
693 | }
694 |
695 | if (kc.timeSkew != null) {
696 | console.info('[KEYCLOAK] Estimated time difference between browser and server is ' + kc.timeSkew + ' seconds');
697 |
698 | if (kc.onTokenExpired) {
699 | var expiresIn = (kc.tokenParsed['exp'] - (new Date().getTime() / 1000) + kc.timeSkew) * 1000;
700 | console.info('[KEYCLOAK] Token expires in ' + Math.round(expiresIn / 1000) + ' s');
701 | if (expiresIn <= 0) {
702 | kc.onTokenExpired();
703 | } else {
704 | kc.tokenTimeoutHandle = setTimeout(kc.onTokenExpired, expiresIn);
705 | }
706 | }
707 | }
708 | } else {
709 | delete kc.token;
710 | delete kc.tokenParsed;
711 | delete kc.subject;
712 | delete kc.realmAccess;
713 | delete kc.resourceAccess;
714 |
715 | kc.authenticated = false;
716 | }
717 | }
718 |
719 | function decodeToken(str) {
720 | str = str.split('.')[1];
721 |
722 | str = str.replace('/-/g', '+');
723 | str = str.replace('/_/g', '/');
724 | switch (str.length % 4)
725 | {
726 | case 0:
727 | break;
728 | case 2:
729 | str += '==';
730 | break;
731 | case 3:
732 | str += '=';
733 | break;
734 | default:
735 | throw 'Invalid token';
736 | }
737 |
738 | str = (str + '===').slice(0, str.length + (str.length % 4));
739 | str = str.replace(/-/g, '+').replace(/_/g, '/');
740 |
741 | str = decodeURIComponent(escape(atob(str)));
742 |
743 | str = JSON.parse(str);
744 | return str;
745 | }
746 |
747 | function createUUID() {
748 | var s = [];
749 | var hexDigits = '0123456789abcdef';
750 | for (var i = 0; i < 36; i++) {
751 | s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
752 | }
753 | s[14] = '4';
754 | s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
755 | s[8] = s[13] = s[18] = s[23] = '-';
756 | var uuid = s.join('');
757 | return uuid;
758 | }
759 |
760 | kc.callback_id = 0;
761 |
762 | function parseCallback(url) {
763 | var oauth = new CallbackParser(url, kc.responseMode).parseUri();
764 | var oauthState = callbackStorage.get(oauth.state);
765 |
766 | if (oauthState && (oauth.code || oauth.error || oauth.access_token || oauth.id_token)) {
767 | oauth.redirectUri = oauthState.redirectUri;
768 | oauth.storedNonce = oauthState.nonce;
769 | oauth.prompt = oauthState.prompt;
770 |
771 | if (oauth.fragment) {
772 | oauth.newUrl += '#' + oauth.fragment;
773 | }
774 |
775 | return oauth;
776 | }
777 | }
778 |
779 | function createPromise() {
780 | var p = {
781 | setSuccess: function(result) {
782 | p.success = true;
783 | p.result = result;
784 | if (p.successCallback) {
785 | p.successCallback(result);
786 | }
787 | },
788 |
789 | setError: function(result) {
790 | p.error = true;
791 | p.result = result;
792 | if (p.errorCallback) {
793 | p.errorCallback(result);
794 | }
795 | },
796 |
797 | promise: {
798 | success: function(callback) {
799 | if (p.success) {
800 | callback(p.result);
801 | } else if (!p.error) {
802 | p.successCallback = callback;
803 | }
804 | return p.promise;
805 | },
806 | error: function(callback) {
807 | if (p.error) {
808 | callback(p.result);
809 | } else if (!p.success) {
810 | p.errorCallback = callback;
811 | }
812 | return p.promise;
813 | }
814 | }
815 | };
816 | return p;
817 | }
818 |
819 | function setupCheckLoginIframe() {
820 | var promise = createPromise();
821 |
822 | if (!loginIframe.enable) {
823 | promise.setSuccess();
824 | return promise.promise;
825 | }
826 |
827 | if (loginIframe.iframe) {
828 | promise.setSuccess();
829 | return promise.promise;
830 | }
831 |
832 | var iframe = document.createElement('iframe');
833 | loginIframe.iframe = iframe;
834 |
835 | iframe.onload = function() {
836 | var realmUrl = getRealmUrl();
837 | if (realmUrl.charAt(0) === '/') {
838 | loginIframe.iframeOrigin = getOrigin();
839 | } else {
840 | loginIframe.iframeOrigin = realmUrl.substring(0, realmUrl.indexOf('/', 8));
841 | }
842 | promise.setSuccess();
843 |
844 | setTimeout(check, loginIframe.interval * 1000);
845 | };
846 |
847 | var src = getRealmUrl() + '/protocol/openid-connect/login-status-iframe.html';
848 | if (kc.iframeVersion) {
849 | src = src + '?version=' + kc.iframeVersion;
850 | }
851 |
852 | iframe.setAttribute('src', src );
853 | iframe.setAttribute('title', 'keycloak-session-iframe' );
854 | iframe.style.display = 'none';
855 | document.body.appendChild(iframe);
856 |
857 | var messageCallback = function(event) {
858 | if ((event.origin !== loginIframe.iframeOrigin) || (loginIframe.iframe.contentWindow !== event.source)) {
859 | return;
860 | }
861 |
862 | if (!(event.data == 'unchanged' || event.data == 'changed' || event.data == 'error')) {
863 | return;
864 | }
865 |
866 |
867 | if (event.data != 'unchanged') {
868 | kc.clearToken();
869 | }
870 |
871 | var callbacks = loginIframe.callbackList.splice(0, loginIframe.callbackList.length);
872 |
873 | for (var i = callbacks.length - 1; i >= 0; --i) {
874 | var promise = callbacks[i];
875 | if (event.data == 'unchanged') {
876 | promise.setSuccess();
877 | } else {
878 | promise.setError();
879 | }
880 | }
881 | };
882 |
883 | window.addEventListener('message', messageCallback, false);
884 |
885 | var check = function() {
886 | checkLoginIframe();
887 | if (kc.token) {
888 | setTimeout(check, loginIframe.interval * 1000);
889 | }
890 | };
891 |
892 | return promise.promise;
893 | }
894 |
895 | function checkLoginIframe() {
896 | var promise = createPromise();
897 |
898 | if (loginIframe.iframe && loginIframe.iframeOrigin ) {
899 | var msg = kc.clientId + ' ' + kc.sessionId;
900 | loginIframe.callbackList.push(promise);
901 | var origin = loginIframe.iframeOrigin;
902 | if (loginIframe.callbackList.length == 1) {
903 | loginIframe.iframe.contentWindow.postMessage(msg, origin);
904 | }
905 | } else {
906 | promise.setSuccess();
907 | }
908 |
909 | return promise.promise;
910 | }
911 |
912 | function loadAdapter(type) {
913 | if (!type || type == 'default') {
914 | return {
915 | login: function(options) {
916 | window.location.href = kc.createLoginUrl(options);
917 | return createPromise().promise;
918 | },
919 |
920 | logout: function(options) {
921 | window.location.href = kc.createLogoutUrl(options);
922 | return createPromise().promise;
923 | },
924 |
925 | register: function(options) {
926 | window.location.href = kc.createRegisterUrl(options);
927 | return createPromise().promise;
928 | },
929 |
930 | accountManagement : function() {
931 | window.location.href = kc.createAccountUrl();
932 | return createPromise().promise;
933 | },
934 |
935 | redirectUri: function(options, encodeHash) {
936 | if (arguments.length == 1) {
937 | encodeHash = true;
938 | }
939 |
940 | if (options && options.redirectUri) {
941 | return options.redirectUri;
942 | } else if (kc.redirectUri) {
943 | return kc.redirectUri;
944 | } else {
945 | var redirectUri = location.href;
946 | if (location.hash && encodeHash) {
947 | redirectUri = redirectUri.substring(0, location.href.indexOf('#'));
948 | redirectUri += (redirectUri.indexOf('?') == -1 ? '?' : '&') + 'redirect_fragment=' + encodeURIComponent(location.hash.substring(1));
949 | }
950 | return redirectUri;
951 | }
952 | }
953 | };
954 | }
955 |
956 | if (type == 'cordova') {
957 | loginIframe.enable = false;
958 | var cordovaOpenWindowWrapper = function(loginUrl, target, options) {
959 | if (window.cordova && window.cordova.InAppBrowser) {
960 | // Use inappbrowser for IOS and Android if available
961 | return window.cordova.InAppBrowser.open(loginUrl, target, options);
962 | } else {
963 | return window.open(loginUrl, target, options);
964 | }
965 | };
966 | return {
967 | login: function(options) {
968 | var promise = createPromise();
969 |
970 | var o = 'location=no';
971 | if (options && options.prompt == 'none') {
972 | o += ',hidden=yes';
973 | }
974 |
975 | var loginUrl = kc.createLoginUrl(options);
976 | var ref = cordovaOpenWindowWrapper(loginUrl, '_blank', o);
977 | var completed = false;
978 |
979 | ref.addEventListener('loadstart', function(event) {
980 | if (event.url.indexOf('http://localhost') == 0) {
981 | var callback = parseCallback(event.url);
982 | processCallback(callback, promise);
983 | ref.close();
984 | completed = true;
985 | }
986 | });
987 |
988 | ref.addEventListener('loaderror', function(event) {
989 | if (!completed) {
990 | if (event.url.indexOf('http://localhost') == 0) {
991 | var callback = parseCallback(event.url);
992 | processCallback(callback, promise);
993 | ref.close();
994 | completed = true;
995 | } else {
996 | promise.setError();
997 | ref.close();
998 | }
999 | }
1000 | });
1001 |
1002 | return promise.promise;
1003 | },
1004 |
1005 | logout: function(options) {
1006 | var promise = createPromise();
1007 |
1008 | var logoutUrl = kc.createLogoutUrl(options);
1009 | var ref = cordovaOpenWindowWrapper(logoutUrl, '_blank', 'location=no,hidden=yes');
1010 |
1011 | var error;
1012 |
1013 | ref.addEventListener('loadstart', function(event) {
1014 | if (event.url.indexOf('http://localhost') == 0) {
1015 | ref.close();
1016 | }
1017 | });
1018 |
1019 | ref.addEventListener('loaderror', function(event) {
1020 | if (event.url.indexOf('http://localhost') == 0) {
1021 | ref.close();
1022 | } else {
1023 | error = true;
1024 | ref.close();
1025 | }
1026 | });
1027 |
1028 | ref.addEventListener('exit', function(event) {
1029 | if (error) {
1030 | promise.setError();
1031 | } else {
1032 | kc.clearToken();
1033 | promise.setSuccess();
1034 | }
1035 | });
1036 |
1037 | return promise.promise;
1038 | },
1039 |
1040 | register : function() {
1041 | var registerUrl = kc.createRegisterUrl();
1042 | var ref = cordovaOpenWindowWrapper(registerUrl, '_blank', 'location=no');
1043 | ref.addEventListener('loadstart', function(event) {
1044 | if (event.url.indexOf('http://localhost') == 0) {
1045 | ref.close();
1046 | }
1047 | });
1048 | },
1049 |
1050 | accountManagement : function() {
1051 | var accountUrl = kc.createAccountUrl();
1052 | var ref = cordovaOpenWindowWrapper(accountUrl, '_blank', 'location=no');
1053 | ref.addEventListener('loadstart', function(event) {
1054 | if (event.url.indexOf('http://localhost') == 0) {
1055 | ref.close();
1056 | }
1057 | });
1058 | },
1059 |
1060 | redirectUri: function(options) {
1061 | return 'http://localhost';
1062 | }
1063 | }
1064 | }
1065 |
1066 | throw 'invalid adapter type: ' + type;
1067 | }
1068 |
1069 | var LocalStorage = function() {
1070 | if (!(this instanceof LocalStorage)) {
1071 | return new LocalStorage();
1072 | }
1073 |
1074 | localStorage.setItem('kc-test', 'test');
1075 | localStorage.removeItem('kc-test');
1076 |
1077 | var cs = this;
1078 |
1079 | function clearExpired() {
1080 | var time = new Date().getTime();
1081 | for (var i = 0; i < localStorage.length; i++) {
1082 | var key = localStorage.key(i);
1083 | if (key && key.indexOf('kc-callback-') == 0) {
1084 | var value = localStorage.getItem(key);
1085 | if (value) {
1086 | try {
1087 | var expires = JSON.parse(value).expires;
1088 | if (!expires || expires < time) {
1089 | localStorage.removeItem(key);
1090 | }
1091 | } catch (err) {
1092 | localStorage.removeItem(key);
1093 | }
1094 | }
1095 | }
1096 | }
1097 | }
1098 |
1099 | cs.get = function(state) {
1100 | if (!state) {
1101 | return;
1102 | }
1103 |
1104 | var key = 'kc-callback-' + state;
1105 | var value = localStorage.getItem(key);
1106 | if (value) {
1107 | localStorage.removeItem(key);
1108 | value = JSON.parse(value);
1109 | }
1110 |
1111 | clearExpired();
1112 | return value;
1113 | };
1114 |
1115 | cs.add = function(state) {
1116 | clearExpired();
1117 |
1118 | var key = 'kc-callback-' + state.state;
1119 | state.expires = new Date().getTime() + (60 * 60 * 1000);
1120 | localStorage.setItem(key, JSON.stringify(state));
1121 | };
1122 | };
1123 |
1124 | var CookieStorage = function() {
1125 | if (!(this instanceof CookieStorage)) {
1126 | return new CookieStorage();
1127 | }
1128 |
1129 | var cs = this;
1130 |
1131 | cs.get = function(state) {
1132 | if (!state) {
1133 | return;
1134 | }
1135 |
1136 | var value = getCookie('kc-callback-' + state);
1137 | setCookie('kc-callback-' + state, '', cookieExpiration(-100));
1138 | if (value) {
1139 | return JSON.parse(value);
1140 | }
1141 | };
1142 |
1143 | cs.add = function(state) {
1144 | setCookie('kc-callback-' + state.state, JSON.stringify(state), cookieExpiration(60));
1145 | };
1146 |
1147 | cs.removeItem = function(key) {
1148 | setCookie(key, '', cookieExpiration(-100));
1149 | };
1150 |
1151 | var cookieExpiration = function (minutes) {
1152 | var exp = new Date();
1153 | exp.setTime(exp.getTime() + (minutes*60*1000));
1154 | return exp;
1155 | };
1156 |
1157 | var getCookie = function (key) {
1158 | var name = key + '=';
1159 | var ca = document.cookie.split(';');
1160 | for (var i = 0; i < ca.length; i++) {
1161 | var c = ca[i];
1162 | while (c.charAt(0) == ' ') {
1163 | c = c.substring(1);
1164 | }
1165 | if (c.indexOf(name) == 0) {
1166 | return c.substring(name.length, c.length);
1167 | }
1168 | }
1169 | return '';
1170 | };
1171 |
1172 | var setCookie = function (key, value, expirationDate) {
1173 | var cookie = key + '=' + value + '; '
1174 | + 'expires=' + expirationDate.toUTCString() + '; ';
1175 | document.cookie = cookie;
1176 | };
1177 | };
1178 |
1179 | function createCallbackStorage() {
1180 | try {
1181 | return new LocalStorage();
1182 | } catch (err) {
1183 | }
1184 |
1185 | return new CookieStorage();
1186 | }
1187 |
1188 | var CallbackParser = function(uriToParse, responseMode) {
1189 | if (!(this instanceof CallbackParser)) {
1190 | return new CallbackParser(uriToParse, responseMode);
1191 | }
1192 | var parser = this;
1193 |
1194 | var initialParse = function() {
1195 | var baseUri = null;
1196 | var queryString = null;
1197 | var fragmentString = null;
1198 |
1199 | var questionMarkIndex = uriToParse.indexOf("?");
1200 | var fragmentIndex = uriToParse.indexOf("#", questionMarkIndex + 1);
1201 | if (questionMarkIndex == -1 && fragmentIndex == -1) {
1202 | baseUri = uriToParse;
1203 | } else if (questionMarkIndex != -1) {
1204 | baseUri = uriToParse.substring(0, questionMarkIndex);
1205 | queryString = uriToParse.substring(questionMarkIndex + 1);
1206 | if (fragmentIndex != -1) {
1207 | fragmentIndex = queryString.indexOf("#");
1208 | fragmentString = queryString.substring(fragmentIndex + 1);
1209 | queryString = queryString.substring(0, fragmentIndex);
1210 | }
1211 | } else {
1212 | baseUri = uriToParse.substring(0, fragmentIndex);
1213 | fragmentString = uriToParse.substring(fragmentIndex + 1);
1214 | }
1215 |
1216 | return { baseUri: baseUri, queryString: queryString, fragmentString: fragmentString };
1217 | };
1218 |
1219 | var parseParams = function(paramString) {
1220 | var result = {};
1221 | var params = paramString.split('&');
1222 | for (var i = 0; i < params.length; i++) {
1223 | var p = params[i].split('=');
1224 | var paramName = decodeURIComponent(p[0]);
1225 | var paramValue = decodeURIComponent(p[1]);
1226 | result[paramName] = paramValue;
1227 | }
1228 | return result;
1229 | };
1230 |
1231 | var handleQueryParam = function(paramName, paramValue, oauth) {
1232 | var supportedOAuthParams = [ 'code', 'state', 'error', 'error_description' ];
1233 |
1234 | for (var i = 0 ; i< supportedOAuthParams.length ; i++) {
1235 | if (paramName === supportedOAuthParams[i]) {
1236 | oauth[paramName] = paramValue;
1237 | return true;
1238 | }
1239 | }
1240 | return false;
1241 | };
1242 |
1243 |
1244 | parser.parseUri = function() {
1245 | var parsedUri = initialParse();
1246 |
1247 | var queryParams = {};
1248 | if (parsedUri.queryString) {
1249 | queryParams = parseParams(parsedUri.queryString);
1250 | }
1251 |
1252 | var oauth = { newUrl: parsedUri.baseUri };
1253 | for (var param in queryParams) {
1254 | switch (param) {
1255 | case 'redirect_fragment':
1256 | oauth.fragment = queryParams[param];
1257 | break;
1258 | default:
1259 | if (responseMode != 'query' || !handleQueryParam(param, queryParams[param], oauth)) {
1260 | oauth.newUrl += (oauth.newUrl.indexOf('?') == -1 ? '?' : '&') + param + '=' + encodeURIComponent(queryParams[param]);
1261 | }
1262 | break;
1263 | }
1264 | }
1265 |
1266 | if (responseMode === 'fragment') {
1267 | var fragmentParams = {};
1268 | if (parsedUri.fragmentString) {
1269 | fragmentParams = parseParams(parsedUri.fragmentString);
1270 | }
1271 | for (var param in fragmentParams) {
1272 | oauth[param] = fragmentParams[param];
1273 | }
1274 | }
1275 |
1276 | return oauth;
1277 | };
1278 | };
1279 |
1280 | };
1281 |
1282 | if ( 'object' === "object" && module && 'object' === "object" ) {
1283 | module.exports = Keycloak;
1284 | } else {
1285 | window.Keycloak = Keycloak;
1286 |
1287 | if ( typeof undefined === "function" && undefined.amd ) {
1288 | undefined( "keycloak", [], function () { return Keycloak; } );
1289 | }
1290 | }
1291 | })( window );
1292 | });
1293 |
1294 | /* */
1295 | var installed = false;
1296 |
1297 | var VueKeyCloak = function VueKeyCloak () {};
1298 |
1299 | VueKeyCloak.install = function (Vue, options) {
1300 | if ( options === void 0 ) options = {
1301 | keycloakOptions: {},
1302 | keycloakInitOptions: {},
1303 | refreshTime: 10
1304 | };
1305 |
1306 | if (installed) { return }
1307 | installed = true;
1308 |
1309 | var keycloak$$1 = keycloak(options.keycloakOptions);
1310 |
1311 | var watch = new Vue({
1312 | data: function data () {
1313 | return {
1314 | ready: false,
1315 | authenticated: false,
1316 | user: null,
1317 | token: null,
1318 | resourceAccess: null
1319 | }
1320 | }
1321 | });
1322 |
1323 | keycloak$$1.init(options.keycloakInitOptions).success(function (isAuthenticated) {
1324 | updateWatchVariables(isAuthenticated).then(function () {
1325 | watch.ready = true;
1326 | });
1327 |
1328 | if (isAuthenticated) {
1329 | setInterval(function () {
1330 | keycloak$$1.updateToken(options.refreshTime + 2)
1331 | .success(function (refreshed) {
1332 | if (refreshed) { updateWatchVariables(true); }
1333 | });
1334 | }, options.refreshTime * 1000);
1335 | }
1336 | });
1337 |
1338 | function updateWatchVariables (isAuthenticated) {
1339 | if ( isAuthenticated === void 0 ) isAuthenticated = false;
1340 |
1341 | watch.authenticated = isAuthenticated;
1342 |
1343 | if (isAuthenticated) {
1344 | watch.token = keycloak$$1.token;
1345 | watch.resourceAccess = keycloak$$1.resourceAccess;
1346 | return new Promise(function (resolve, reject) {
1347 | keycloak$$1.loadUserProfile().success(function (user) {
1348 | watch.user = user;
1349 | resolve();
1350 | });
1351 | })
1352 | } else {
1353 | return Promise.resolve()
1354 | }
1355 | }
1356 |
1357 | Object.defineProperty(Vue.prototype, '$keycloak', {
1358 | get: function get () {
1359 | keycloak$$1.ready = watch.ready;
1360 | keycloak$$1.user = watch.user;
1361 | return keycloak$$1
1362 | }
1363 | });
1364 | };
1365 |
1366 | VueKeyCloak.version = '0.0.11';
1367 |
1368 | module.exports = VueKeyCloak;
1369 |
--------------------------------------------------------------------------------
/dist/vue-keycloak.js:
--------------------------------------------------------------------------------
1 | /**
2 | * vue-keycloak v0.0.11
3 | * (c) Cristian Baldi 2018
4 | */
5 | (function (global, factory) {
6 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
7 | typeof define === 'function' && define.amd ? define(factory) :
8 | (global.VueKeycloak = factory());
9 | }(this, (function () { 'use strict';
10 |
11 | function createCommonjsModule(fn, module) {
12 | return module = { exports: {} }, fn(module, module.exports), module.exports;
13 | }
14 |
15 | var keycloak = createCommonjsModule(function (module) {
16 | /*
17 | * Copyright 2016 Red Hat, Inc. and/or its affiliates
18 | * and other contributors as indicated by the @author tags.
19 | *
20 | * Licensed under the Apache License, Version 2.0 (the "License");
21 | * you may not use this file except in compliance with the License.
22 | * You may obtain a copy of the License at
23 | *
24 | * http://www.apache.org/licenses/LICENSE-2.0
25 | *
26 | * Unless required by applicable law or agreed to in writing, software
27 | * distributed under the License is distributed on an "AS IS" BASIS,
28 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29 | * See the License for the specific language governing permissions and
30 | * limitations under the License.
31 | */
32 |
33 | (function( window, undefined ) {
34 |
35 | var Keycloak = function (config) {
36 | if (!(this instanceof Keycloak)) {
37 | return new Keycloak(config);
38 | }
39 |
40 | var kc = this;
41 | var adapter;
42 | var refreshQueue = [];
43 | var callbackStorage;
44 |
45 | var loginIframe = {
46 | enable: true,
47 | callbackList: [],
48 | interval: 5
49 | };
50 |
51 | var scripts = document.getElementsByTagName('script');
52 | for (var i = 0; i < scripts.length; i++) {
53 | if ((scripts[i].src.indexOf('keycloak.js') !== -1 || scripts[i].src.indexOf('keycloak.min.js') !== -1) && scripts[i].src.indexOf('version=') !== -1) {
54 | kc.iframeVersion = scripts[i].src.substring(scripts[i].src.indexOf('version=') + 8).split('&')[0];
55 | }
56 | }
57 |
58 | kc.init = function (initOptions) {
59 | kc.authenticated = false;
60 |
61 | callbackStorage = createCallbackStorage();
62 |
63 | if (initOptions && initOptions.adapter === 'cordova') {
64 | adapter = loadAdapter('cordova');
65 | } else if (initOptions && initOptions.adapter === 'default') {
66 | adapter = loadAdapter();
67 | } else {
68 | if (window.Cordova || window.cordova) {
69 | adapter = loadAdapter('cordova');
70 | } else {
71 | adapter = loadAdapter();
72 | }
73 | }
74 |
75 | if (initOptions) {
76 | if (typeof initOptions.checkLoginIframe !== 'undefined') {
77 | loginIframe.enable = initOptions.checkLoginIframe;
78 | }
79 |
80 | if (initOptions.checkLoginIframeInterval) {
81 | loginIframe.interval = initOptions.checkLoginIframeInterval;
82 | }
83 |
84 | if (initOptions.onLoad === 'login-required') {
85 | kc.loginRequired = true;
86 | }
87 |
88 | if (initOptions.responseMode) {
89 | if (initOptions.responseMode === 'query' || initOptions.responseMode === 'fragment') {
90 | kc.responseMode = initOptions.responseMode;
91 | } else {
92 | throw 'Invalid value for responseMode';
93 | }
94 | }
95 |
96 | if (initOptions.flow) {
97 | switch (initOptions.flow) {
98 | case 'standard':
99 | kc.responseType = 'code';
100 | break;
101 | case 'implicit':
102 | kc.responseType = 'id_token token';
103 | break;
104 | case 'hybrid':
105 | kc.responseType = 'code id_token token';
106 | break;
107 | default:
108 | throw 'Invalid value for flow';
109 | }
110 | kc.flow = initOptions.flow;
111 | }
112 |
113 | if (initOptions.timeSkew != null) {
114 | kc.timeSkew = initOptions.timeSkew;
115 | }
116 | }
117 |
118 | if (!kc.responseMode) {
119 | kc.responseMode = 'fragment';
120 | }
121 | if (!kc.responseType) {
122 | kc.responseType = 'code';
123 | kc.flow = 'standard';
124 | }
125 |
126 | var promise = createPromise();
127 |
128 | var initPromise = createPromise();
129 | initPromise.promise.success(function() {
130 | kc.onReady && kc.onReady(kc.authenticated);
131 | promise.setSuccess(kc.authenticated);
132 | }).error(function(errorData) {
133 | promise.setError(errorData);
134 | });
135 |
136 | var configPromise = loadConfig(config);
137 |
138 | function onLoad() {
139 | var doLogin = function(prompt) {
140 | if (!prompt) {
141 | options.prompt = 'none';
142 | }
143 | kc.login(options).success(function () {
144 | initPromise.setSuccess();
145 | }).error(function () {
146 | initPromise.setError();
147 | });
148 | };
149 |
150 | var options = {};
151 | switch (initOptions.onLoad) {
152 | case 'check-sso':
153 | if (loginIframe.enable) {
154 | setupCheckLoginIframe().success(function() {
155 | checkLoginIframe().success(function () {
156 | doLogin(false);
157 | }).error(function () {
158 | initPromise.setSuccess();
159 | });
160 | });
161 | } else {
162 | doLogin(false);
163 | }
164 | break;
165 | case 'login-required':
166 | doLogin(true);
167 | break;
168 | default:
169 | throw 'Invalid value for onLoad';
170 | }
171 | }
172 |
173 | function processInit() {
174 | var callback = parseCallback(window.location.href);
175 |
176 | if (callback) {
177 | return setupCheckLoginIframe().success(function() {
178 | window.history.replaceState({}, null, callback.newUrl);
179 | processCallback(callback, initPromise);
180 | }).error(function (e) {
181 | initPromise.setError();
182 | });
183 | } else if (initOptions) {
184 | if (initOptions.token && initOptions.refreshToken) {
185 | setToken(initOptions.token, initOptions.refreshToken, initOptions.idToken);
186 |
187 | if (loginIframe.enable) {
188 | setupCheckLoginIframe().success(function() {
189 | checkLoginIframe().success(function () {
190 | kc.onAuthSuccess && kc.onAuthSuccess();
191 | initPromise.setSuccess();
192 | }).error(function () {
193 | setToken(null, null, null);
194 | initPromise.setSuccess();
195 | });
196 | });
197 | } else {
198 | kc.updateToken(-1).success(function() {
199 | kc.onAuthSuccess && kc.onAuthSuccess();
200 | initPromise.setSuccess();
201 | }).error(function() {
202 | kc.onAuthError && kc.onAuthError();
203 | if (initOptions.onLoad) {
204 | onLoad();
205 | } else {
206 | initPromise.setError();
207 | }
208 | });
209 | }
210 | } else if (initOptions.onLoad) {
211 | onLoad();
212 | } else {
213 | initPromise.setSuccess();
214 | }
215 | } else {
216 | initPromise.setSuccess();
217 | }
218 | }
219 |
220 | configPromise.success(processInit);
221 | configPromise.error(function() {
222 | promise.setError();
223 | });
224 |
225 | return promise.promise;
226 | };
227 |
228 | kc.login = function (options) {
229 | return adapter.login(options);
230 | };
231 |
232 | kc.createLoginUrl = function(options) {
233 | var state = createUUID();
234 | var nonce = createUUID();
235 |
236 | var redirectUri = adapter.redirectUri(options);
237 |
238 | var callbackState = {
239 | state: state,
240 | nonce: nonce,
241 | redirectUri: encodeURIComponent(redirectUri)
242 | };
243 |
244 | if (options && options.prompt) {
245 | callbackState.prompt = options.prompt;
246 | }
247 |
248 | callbackStorage.add(callbackState);
249 |
250 | var action = 'auth';
251 | if (options && options.action == 'register') {
252 | action = 'registrations';
253 | }
254 |
255 | var scope = (options && options.scope) ? "openid " + options.scope : "openid";
256 |
257 | var url = getRealmUrl()
258 | + '/protocol/openid-connect/' + action
259 | + '?client_id=' + encodeURIComponent(kc.clientId)
260 | + '&redirect_uri=' + encodeURIComponent(redirectUri)
261 | + '&state=' + encodeURIComponent(state)
262 | + '&nonce=' + encodeURIComponent(nonce)
263 | + '&response_mode=' + encodeURIComponent(kc.responseMode)
264 | + '&response_type=' + encodeURIComponent(kc.responseType)
265 | + '&scope=' + encodeURIComponent(scope);
266 |
267 | if (options && options.prompt) {
268 | url += '&prompt=' + encodeURIComponent(options.prompt);
269 | }
270 |
271 | if (options && options.maxAge) {
272 | url += '&max_age=' + encodeURIComponent(options.maxAge);
273 | }
274 |
275 | if (options && options.loginHint) {
276 | url += '&login_hint=' + encodeURIComponent(options.loginHint);
277 | }
278 |
279 | if (options && options.idpHint) {
280 | url += '&kc_idp_hint=' + encodeURIComponent(options.idpHint);
281 | }
282 |
283 | if (options && options.locale) {
284 | url += '&ui_locales=' + encodeURIComponent(options.locale);
285 | }
286 |
287 | return url;
288 | };
289 |
290 | kc.logout = function(options) {
291 | return adapter.logout(options);
292 | };
293 |
294 | kc.createLogoutUrl = function(options) {
295 | var url = getRealmUrl()
296 | + '/protocol/openid-connect/logout'
297 | + '?redirect_uri=' + encodeURIComponent(adapter.redirectUri(options, false));
298 |
299 | return url;
300 | };
301 |
302 | kc.register = function (options) {
303 | return adapter.register(options);
304 | };
305 |
306 | kc.createRegisterUrl = function(options) {
307 | if (!options) {
308 | options = {};
309 | }
310 | options.action = 'register';
311 | return kc.createLoginUrl(options);
312 | };
313 |
314 | kc.createAccountUrl = function(options) {
315 | var url = getRealmUrl()
316 | + '/account'
317 | + '?referrer=' + encodeURIComponent(kc.clientId)
318 | + '&referrer_uri=' + encodeURIComponent(adapter.redirectUri(options));
319 |
320 | return url;
321 | };
322 |
323 | kc.accountManagement = function() {
324 | return adapter.accountManagement();
325 | };
326 |
327 | kc.hasRealmRole = function (role) {
328 | var access = kc.realmAccess;
329 | return !!access && access.roles.indexOf(role) >= 0;
330 | };
331 |
332 | kc.hasResourceRole = function(role, resource) {
333 | if (!kc.resourceAccess) {
334 | return false;
335 | }
336 |
337 | var access = kc.resourceAccess[resource || kc.clientId];
338 | return !!access && access.roles.indexOf(role) >= 0;
339 | };
340 |
341 | kc.loadUserProfile = function() {
342 | var url = getRealmUrl() + '/account';
343 | var req = new XMLHttpRequest();
344 | req.open('GET', url, true);
345 | req.setRequestHeader('Accept', 'application/json');
346 | req.setRequestHeader('Authorization', 'bearer ' + kc.token);
347 |
348 | var promise = createPromise();
349 |
350 | req.onreadystatechange = function () {
351 | if (req.readyState == 4) {
352 | if (req.status == 200) {
353 | kc.profile = JSON.parse(req.responseText);
354 | promise.setSuccess(kc.profile);
355 | } else {
356 | promise.setError();
357 | }
358 | }
359 | };
360 |
361 | req.send();
362 |
363 | return promise.promise;
364 | };
365 |
366 | kc.loadUserInfo = function() {
367 | var url = getRealmUrl() + '/protocol/openid-connect/userinfo';
368 | var req = new XMLHttpRequest();
369 | req.open('GET', url, true);
370 | req.setRequestHeader('Accept', 'application/json');
371 | req.setRequestHeader('Authorization', 'bearer ' + kc.token);
372 |
373 | var promise = createPromise();
374 |
375 | req.onreadystatechange = function () {
376 | if (req.readyState == 4) {
377 | if (req.status == 200) {
378 | kc.userInfo = JSON.parse(req.responseText);
379 | promise.setSuccess(kc.userInfo);
380 | } else {
381 | promise.setError();
382 | }
383 | }
384 | };
385 |
386 | req.send();
387 |
388 | return promise.promise;
389 | };
390 |
391 | kc.isTokenExpired = function(minValidity) {
392 | if (!kc.tokenParsed || (!kc.refreshToken && kc.flow != 'implicit' )) {
393 | throw 'Not authenticated';
394 | }
395 |
396 | if (kc.timeSkew == null) {
397 | console.info('[KEYCLOAK] Unable to determine if token is expired as timeskew is not set');
398 | return true;
399 | }
400 |
401 | var expiresIn = kc.tokenParsed['exp'] - Math.ceil(new Date().getTime() / 1000) + kc.timeSkew;
402 | if (minValidity) {
403 | expiresIn -= minValidity;
404 | }
405 | return expiresIn < 0;
406 | };
407 |
408 | kc.updateToken = function(minValidity) {
409 | var promise = createPromise();
410 |
411 | if (!kc.refreshToken) {
412 | promise.setError();
413 | return promise.promise;
414 | }
415 |
416 | minValidity = minValidity || 5;
417 |
418 | var exec = function() {
419 | var refreshToken = false;
420 | if (minValidity == -1) {
421 | refreshToken = true;
422 | console.info('[KEYCLOAK] Refreshing token: forced refresh');
423 | } else if (!kc.tokenParsed || kc.isTokenExpired(minValidity)) {
424 | refreshToken = true;
425 | console.info('[KEYCLOAK] Refreshing token: token expired');
426 | }
427 |
428 | if (!refreshToken) {
429 | promise.setSuccess(false);
430 | } else {
431 | var params = 'grant_type=refresh_token&' + 'refresh_token=' + kc.refreshToken;
432 | var url = getRealmUrl() + '/protocol/openid-connect/token';
433 |
434 | refreshQueue.push(promise);
435 |
436 | if (refreshQueue.length == 1) {
437 | var req = new XMLHttpRequest();
438 | req.open('POST', url, true);
439 | req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
440 | req.withCredentials = true;
441 |
442 | if (kc.clientId && kc.clientSecret) {
443 | req.setRequestHeader('Authorization', 'Basic ' + btoa(kc.clientId + ':' + kc.clientSecret));
444 | } else {
445 | params += '&client_id=' + encodeURIComponent(kc.clientId);
446 | }
447 |
448 | var timeLocal = new Date().getTime();
449 |
450 | req.onreadystatechange = function () {
451 | if (req.readyState == 4) {
452 | if (req.status == 200) {
453 | console.info('[KEYCLOAK] Token refreshed');
454 |
455 | timeLocal = (timeLocal + new Date().getTime()) / 2;
456 |
457 | var tokenResponse = JSON.parse(req.responseText);
458 |
459 | setToken(tokenResponse['access_token'], tokenResponse['refresh_token'], tokenResponse['id_token'], timeLocal);
460 |
461 | kc.onAuthRefreshSuccess && kc.onAuthRefreshSuccess();
462 | for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) {
463 | p.setSuccess(true);
464 | }
465 | } else {
466 | console.warn('[KEYCLOAK] Failed to refresh token');
467 |
468 | kc.onAuthRefreshError && kc.onAuthRefreshError();
469 | for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) {
470 | p.setError(true);
471 | }
472 | }
473 | }
474 | };
475 |
476 | req.send(params);
477 | }
478 | }
479 | };
480 |
481 | if (loginIframe.enable) {
482 | var iframePromise = checkLoginIframe();
483 | iframePromise.success(function() {
484 | exec();
485 | }).error(function() {
486 | promise.setError();
487 | });
488 | } else {
489 | exec();
490 | }
491 |
492 | return promise.promise;
493 | };
494 |
495 | kc.clearToken = function() {
496 | if (kc.token) {
497 | setToken(null, null, null);
498 | kc.onAuthLogout && kc.onAuthLogout();
499 | if (kc.loginRequired) {
500 | kc.login();
501 | }
502 | }
503 | };
504 |
505 | function getRealmUrl() {
506 | if (kc.authServerUrl.charAt(kc.authServerUrl.length - 1) == '/') {
507 | return kc.authServerUrl + 'realms/' + encodeURIComponent(kc.realm);
508 | } else {
509 | return kc.authServerUrl + '/realms/' + encodeURIComponent(kc.realm);
510 | }
511 | }
512 |
513 | function getOrigin() {
514 | if (!window.location.origin) {
515 | return window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: '');
516 | } else {
517 | return window.location.origin;
518 | }
519 | }
520 |
521 | function processCallback(oauth, promise) {
522 | var code = oauth.code;
523 | var error = oauth.error;
524 | var prompt = oauth.prompt;
525 |
526 | var timeLocal = new Date().getTime();
527 |
528 | if (error) {
529 | if (prompt != 'none') {
530 | var errorData = { error: error, error_description: oauth.error_description };
531 | kc.onAuthError && kc.onAuthError(errorData);
532 | promise && promise.setError(errorData);
533 | } else {
534 | promise && promise.setSuccess();
535 | }
536 | return;
537 | } else if ((kc.flow != 'standard') && (oauth.access_token || oauth.id_token)) {
538 | authSuccess(oauth.access_token, null, oauth.id_token, true);
539 | }
540 |
541 | if ((kc.flow != 'implicit') && code) {
542 | var params = 'code=' + code + '&grant_type=authorization_code';
543 | var url = getRealmUrl() + '/protocol/openid-connect/token';
544 |
545 | var req = new XMLHttpRequest();
546 | req.open('POST', url, true);
547 | req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
548 |
549 | if (kc.clientId && kc.clientSecret) {
550 | req.setRequestHeader('Authorization', 'Basic ' + btoa(kc.clientId + ':' + kc.clientSecret));
551 | } else {
552 | params += '&client_id=' + encodeURIComponent(kc.clientId);
553 | }
554 |
555 | params += '&redirect_uri=' + oauth.redirectUri;
556 |
557 | req.withCredentials = true;
558 |
559 | req.onreadystatechange = function() {
560 | if (req.readyState == 4) {
561 | if (req.status == 200) {
562 |
563 | var tokenResponse = JSON.parse(req.responseText);
564 | authSuccess(tokenResponse['access_token'], tokenResponse['refresh_token'], tokenResponse['id_token'], kc.flow === 'standard');
565 | } else {
566 | kc.onAuthError && kc.onAuthError();
567 | promise && promise.setError();
568 | }
569 | }
570 | };
571 |
572 | req.send(params);
573 | }
574 |
575 | function authSuccess(accessToken, refreshToken, idToken, fulfillPromise) {
576 | timeLocal = (timeLocal + new Date().getTime()) / 2;
577 |
578 | setToken(accessToken, refreshToken, idToken, timeLocal);
579 |
580 | if ((kc.tokenParsed && kc.tokenParsed.nonce != oauth.storedNonce) ||
581 | (kc.refreshTokenParsed && kc.refreshTokenParsed.nonce != oauth.storedNonce) ||
582 | (kc.idTokenParsed && kc.idTokenParsed.nonce != oauth.storedNonce)) {
583 |
584 | console.info('[KEYCLOAK] Invalid nonce, clearing token');
585 | kc.clearToken();
586 | promise && promise.setError();
587 | } else {
588 | if (fulfillPromise) {
589 | kc.onAuthSuccess && kc.onAuthSuccess();
590 | promise && promise.setSuccess();
591 | }
592 | }
593 | }
594 |
595 | }
596 |
597 | function loadConfig(url) {
598 | var promise = createPromise();
599 | var configUrl;
600 |
601 | if (!config) {
602 | configUrl = 'keycloak.json';
603 | } else if (typeof config === 'string') {
604 | configUrl = config;
605 | }
606 |
607 | if (configUrl) {
608 | var req = new XMLHttpRequest();
609 | req.open('GET', configUrl, true);
610 | req.setRequestHeader('Accept', 'application/json');
611 |
612 | req.onreadystatechange = function () {
613 | if (req.readyState == 4) {
614 | if (req.status == 200 || fileLoaded(req)) {
615 | var config = JSON.parse(req.responseText);
616 |
617 | kc.authServerUrl = config['auth-server-url'];
618 | kc.realm = config['realm'];
619 | kc.clientId = config['resource'];
620 | kc.clientSecret = (config['credentials'] || {})['secret'];
621 |
622 | promise.setSuccess();
623 | } else {
624 | promise.setError();
625 | }
626 | }
627 | };
628 |
629 | req.send();
630 | } else {
631 | if (!config['url']) {
632 | var scripts = document.getElementsByTagName('script');
633 | for (var i = 0; i < scripts.length; i++) {
634 | if (scripts[i].src.match(/.*keycloak\.js/)) {
635 | config.url = scripts[i].src.substr(0, scripts[i].src.indexOf('/js/keycloak.js'));
636 | break;
637 | }
638 | }
639 | }
640 |
641 | if (!config.realm) {
642 | throw 'realm missing';
643 | }
644 |
645 | if (!config.clientId) {
646 | throw 'clientId missing';
647 | }
648 |
649 | kc.authServerUrl = config.url;
650 | kc.realm = config.realm;
651 | kc.clientId = config.clientId;
652 | kc.clientSecret = (config.credentials || {}).secret;
653 |
654 | promise.setSuccess();
655 | }
656 |
657 | return promise.promise;
658 | }
659 |
660 | function fileLoaded(xhr) {
661 | return xhr.status == 0 && xhr.responseText && xhr.responseURL.startsWith('file:');
662 | }
663 |
664 | function setToken(token, refreshToken, idToken, timeLocal) {
665 | if (kc.tokenTimeoutHandle) {
666 | clearTimeout(kc.tokenTimeoutHandle);
667 | kc.tokenTimeoutHandle = null;
668 | }
669 |
670 | if (refreshToken) {
671 | kc.refreshToken = refreshToken;
672 | kc.refreshTokenParsed = decodeToken(refreshToken);
673 | } else {
674 | delete kc.refreshToken;
675 | delete kc.refreshTokenParsed;
676 | }
677 |
678 | if (idToken) {
679 | kc.idToken = idToken;
680 | kc.idTokenParsed = decodeToken(idToken);
681 | } else {
682 | delete kc.idToken;
683 | delete kc.idTokenParsed;
684 | }
685 |
686 | if (token) {
687 | kc.token = token;
688 | kc.tokenParsed = decodeToken(token);
689 | kc.sessionId = kc.tokenParsed.session_state;
690 | kc.authenticated = true;
691 | kc.subject = kc.tokenParsed.sub;
692 | kc.realmAccess = kc.tokenParsed.realm_access;
693 | kc.resourceAccess = kc.tokenParsed.resource_access;
694 |
695 | if (timeLocal) {
696 | kc.timeSkew = Math.floor(timeLocal / 1000) - kc.tokenParsed.iat;
697 | }
698 |
699 | if (kc.timeSkew != null) {
700 | console.info('[KEYCLOAK] Estimated time difference between browser and server is ' + kc.timeSkew + ' seconds');
701 |
702 | if (kc.onTokenExpired) {
703 | var expiresIn = (kc.tokenParsed['exp'] - (new Date().getTime() / 1000) + kc.timeSkew) * 1000;
704 | console.info('[KEYCLOAK] Token expires in ' + Math.round(expiresIn / 1000) + ' s');
705 | if (expiresIn <= 0) {
706 | kc.onTokenExpired();
707 | } else {
708 | kc.tokenTimeoutHandle = setTimeout(kc.onTokenExpired, expiresIn);
709 | }
710 | }
711 | }
712 | } else {
713 | delete kc.token;
714 | delete kc.tokenParsed;
715 | delete kc.subject;
716 | delete kc.realmAccess;
717 | delete kc.resourceAccess;
718 |
719 | kc.authenticated = false;
720 | }
721 | }
722 |
723 | function decodeToken(str) {
724 | str = str.split('.')[1];
725 |
726 | str = str.replace('/-/g', '+');
727 | str = str.replace('/_/g', '/');
728 | switch (str.length % 4)
729 | {
730 | case 0:
731 | break;
732 | case 2:
733 | str += '==';
734 | break;
735 | case 3:
736 | str += '=';
737 | break;
738 | default:
739 | throw 'Invalid token';
740 | }
741 |
742 | str = (str + '===').slice(0, str.length + (str.length % 4));
743 | str = str.replace(/-/g, '+').replace(/_/g, '/');
744 |
745 | str = decodeURIComponent(escape(atob(str)));
746 |
747 | str = JSON.parse(str);
748 | return str;
749 | }
750 |
751 | function createUUID() {
752 | var s = [];
753 | var hexDigits = '0123456789abcdef';
754 | for (var i = 0; i < 36; i++) {
755 | s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
756 | }
757 | s[14] = '4';
758 | s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
759 | s[8] = s[13] = s[18] = s[23] = '-';
760 | var uuid = s.join('');
761 | return uuid;
762 | }
763 |
764 | kc.callback_id = 0;
765 |
766 | function parseCallback(url) {
767 | var oauth = new CallbackParser(url, kc.responseMode).parseUri();
768 | var oauthState = callbackStorage.get(oauth.state);
769 |
770 | if (oauthState && (oauth.code || oauth.error || oauth.access_token || oauth.id_token)) {
771 | oauth.redirectUri = oauthState.redirectUri;
772 | oauth.storedNonce = oauthState.nonce;
773 | oauth.prompt = oauthState.prompt;
774 |
775 | if (oauth.fragment) {
776 | oauth.newUrl += '#' + oauth.fragment;
777 | }
778 |
779 | return oauth;
780 | }
781 | }
782 |
783 | function createPromise() {
784 | var p = {
785 | setSuccess: function(result) {
786 | p.success = true;
787 | p.result = result;
788 | if (p.successCallback) {
789 | p.successCallback(result);
790 | }
791 | },
792 |
793 | setError: function(result) {
794 | p.error = true;
795 | p.result = result;
796 | if (p.errorCallback) {
797 | p.errorCallback(result);
798 | }
799 | },
800 |
801 | promise: {
802 | success: function(callback) {
803 | if (p.success) {
804 | callback(p.result);
805 | } else if (!p.error) {
806 | p.successCallback = callback;
807 | }
808 | return p.promise;
809 | },
810 | error: function(callback) {
811 | if (p.error) {
812 | callback(p.result);
813 | } else if (!p.success) {
814 | p.errorCallback = callback;
815 | }
816 | return p.promise;
817 | }
818 | }
819 | };
820 | return p;
821 | }
822 |
823 | function setupCheckLoginIframe() {
824 | var promise = createPromise();
825 |
826 | if (!loginIframe.enable) {
827 | promise.setSuccess();
828 | return promise.promise;
829 | }
830 |
831 | if (loginIframe.iframe) {
832 | promise.setSuccess();
833 | return promise.promise;
834 | }
835 |
836 | var iframe = document.createElement('iframe');
837 | loginIframe.iframe = iframe;
838 |
839 | iframe.onload = function() {
840 | var realmUrl = getRealmUrl();
841 | if (realmUrl.charAt(0) === '/') {
842 | loginIframe.iframeOrigin = getOrigin();
843 | } else {
844 | loginIframe.iframeOrigin = realmUrl.substring(0, realmUrl.indexOf('/', 8));
845 | }
846 | promise.setSuccess();
847 |
848 | setTimeout(check, loginIframe.interval * 1000);
849 | };
850 |
851 | var src = getRealmUrl() + '/protocol/openid-connect/login-status-iframe.html';
852 | if (kc.iframeVersion) {
853 | src = src + '?version=' + kc.iframeVersion;
854 | }
855 |
856 | iframe.setAttribute('src', src );
857 | iframe.setAttribute('title', 'keycloak-session-iframe' );
858 | iframe.style.display = 'none';
859 | document.body.appendChild(iframe);
860 |
861 | var messageCallback = function(event) {
862 | if ((event.origin !== loginIframe.iframeOrigin) || (loginIframe.iframe.contentWindow !== event.source)) {
863 | return;
864 | }
865 |
866 | if (!(event.data == 'unchanged' || event.data == 'changed' || event.data == 'error')) {
867 | return;
868 | }
869 |
870 |
871 | if (event.data != 'unchanged') {
872 | kc.clearToken();
873 | }
874 |
875 | var callbacks = loginIframe.callbackList.splice(0, loginIframe.callbackList.length);
876 |
877 | for (var i = callbacks.length - 1; i >= 0; --i) {
878 | var promise = callbacks[i];
879 | if (event.data == 'unchanged') {
880 | promise.setSuccess();
881 | } else {
882 | promise.setError();
883 | }
884 | }
885 | };
886 |
887 | window.addEventListener('message', messageCallback, false);
888 |
889 | var check = function() {
890 | checkLoginIframe();
891 | if (kc.token) {
892 | setTimeout(check, loginIframe.interval * 1000);
893 | }
894 | };
895 |
896 | return promise.promise;
897 | }
898 |
899 | function checkLoginIframe() {
900 | var promise = createPromise();
901 |
902 | if (loginIframe.iframe && loginIframe.iframeOrigin ) {
903 | var msg = kc.clientId + ' ' + kc.sessionId;
904 | loginIframe.callbackList.push(promise);
905 | var origin = loginIframe.iframeOrigin;
906 | if (loginIframe.callbackList.length == 1) {
907 | loginIframe.iframe.contentWindow.postMessage(msg, origin);
908 | }
909 | } else {
910 | promise.setSuccess();
911 | }
912 |
913 | return promise.promise;
914 | }
915 |
916 | function loadAdapter(type) {
917 | if (!type || type == 'default') {
918 | return {
919 | login: function(options) {
920 | window.location.href = kc.createLoginUrl(options);
921 | return createPromise().promise;
922 | },
923 |
924 | logout: function(options) {
925 | window.location.href = kc.createLogoutUrl(options);
926 | return createPromise().promise;
927 | },
928 |
929 | register: function(options) {
930 | window.location.href = kc.createRegisterUrl(options);
931 | return createPromise().promise;
932 | },
933 |
934 | accountManagement : function() {
935 | window.location.href = kc.createAccountUrl();
936 | return createPromise().promise;
937 | },
938 |
939 | redirectUri: function(options, encodeHash) {
940 | if (arguments.length == 1) {
941 | encodeHash = true;
942 | }
943 |
944 | if (options && options.redirectUri) {
945 | return options.redirectUri;
946 | } else if (kc.redirectUri) {
947 | return kc.redirectUri;
948 | } else {
949 | var redirectUri = location.href;
950 | if (location.hash && encodeHash) {
951 | redirectUri = redirectUri.substring(0, location.href.indexOf('#'));
952 | redirectUri += (redirectUri.indexOf('?') == -1 ? '?' : '&') + 'redirect_fragment=' + encodeURIComponent(location.hash.substring(1));
953 | }
954 | return redirectUri;
955 | }
956 | }
957 | };
958 | }
959 |
960 | if (type == 'cordova') {
961 | loginIframe.enable = false;
962 | var cordovaOpenWindowWrapper = function(loginUrl, target, options) {
963 | if (window.cordova && window.cordova.InAppBrowser) {
964 | // Use inappbrowser for IOS and Android if available
965 | return window.cordova.InAppBrowser.open(loginUrl, target, options);
966 | } else {
967 | return window.open(loginUrl, target, options);
968 | }
969 | };
970 | return {
971 | login: function(options) {
972 | var promise = createPromise();
973 |
974 | var o = 'location=no';
975 | if (options && options.prompt == 'none') {
976 | o += ',hidden=yes';
977 | }
978 |
979 | var loginUrl = kc.createLoginUrl(options);
980 | var ref = cordovaOpenWindowWrapper(loginUrl, '_blank', o);
981 | var completed = false;
982 |
983 | ref.addEventListener('loadstart', function(event) {
984 | if (event.url.indexOf('http://localhost') == 0) {
985 | var callback = parseCallback(event.url);
986 | processCallback(callback, promise);
987 | ref.close();
988 | completed = true;
989 | }
990 | });
991 |
992 | ref.addEventListener('loaderror', function(event) {
993 | if (!completed) {
994 | if (event.url.indexOf('http://localhost') == 0) {
995 | var callback = parseCallback(event.url);
996 | processCallback(callback, promise);
997 | ref.close();
998 | completed = true;
999 | } else {
1000 | promise.setError();
1001 | ref.close();
1002 | }
1003 | }
1004 | });
1005 |
1006 | return promise.promise;
1007 | },
1008 |
1009 | logout: function(options) {
1010 | var promise = createPromise();
1011 |
1012 | var logoutUrl = kc.createLogoutUrl(options);
1013 | var ref = cordovaOpenWindowWrapper(logoutUrl, '_blank', 'location=no,hidden=yes');
1014 |
1015 | var error;
1016 |
1017 | ref.addEventListener('loadstart', function(event) {
1018 | if (event.url.indexOf('http://localhost') == 0) {
1019 | ref.close();
1020 | }
1021 | });
1022 |
1023 | ref.addEventListener('loaderror', function(event) {
1024 | if (event.url.indexOf('http://localhost') == 0) {
1025 | ref.close();
1026 | } else {
1027 | error = true;
1028 | ref.close();
1029 | }
1030 | });
1031 |
1032 | ref.addEventListener('exit', function(event) {
1033 | if (error) {
1034 | promise.setError();
1035 | } else {
1036 | kc.clearToken();
1037 | promise.setSuccess();
1038 | }
1039 | });
1040 |
1041 | return promise.promise;
1042 | },
1043 |
1044 | register : function() {
1045 | var registerUrl = kc.createRegisterUrl();
1046 | var ref = cordovaOpenWindowWrapper(registerUrl, '_blank', 'location=no');
1047 | ref.addEventListener('loadstart', function(event) {
1048 | if (event.url.indexOf('http://localhost') == 0) {
1049 | ref.close();
1050 | }
1051 | });
1052 | },
1053 |
1054 | accountManagement : function() {
1055 | var accountUrl = kc.createAccountUrl();
1056 | var ref = cordovaOpenWindowWrapper(accountUrl, '_blank', 'location=no');
1057 | ref.addEventListener('loadstart', function(event) {
1058 | if (event.url.indexOf('http://localhost') == 0) {
1059 | ref.close();
1060 | }
1061 | });
1062 | },
1063 |
1064 | redirectUri: function(options) {
1065 | return 'http://localhost';
1066 | }
1067 | }
1068 | }
1069 |
1070 | throw 'invalid adapter type: ' + type;
1071 | }
1072 |
1073 | var LocalStorage = function() {
1074 | if (!(this instanceof LocalStorage)) {
1075 | return new LocalStorage();
1076 | }
1077 |
1078 | localStorage.setItem('kc-test', 'test');
1079 | localStorage.removeItem('kc-test');
1080 |
1081 | var cs = this;
1082 |
1083 | function clearExpired() {
1084 | var time = new Date().getTime();
1085 | for (var i = 0; i < localStorage.length; i++) {
1086 | var key = localStorage.key(i);
1087 | if (key && key.indexOf('kc-callback-') == 0) {
1088 | var value = localStorage.getItem(key);
1089 | if (value) {
1090 | try {
1091 | var expires = JSON.parse(value).expires;
1092 | if (!expires || expires < time) {
1093 | localStorage.removeItem(key);
1094 | }
1095 | } catch (err) {
1096 | localStorage.removeItem(key);
1097 | }
1098 | }
1099 | }
1100 | }
1101 | }
1102 |
1103 | cs.get = function(state) {
1104 | if (!state) {
1105 | return;
1106 | }
1107 |
1108 | var key = 'kc-callback-' + state;
1109 | var value = localStorage.getItem(key);
1110 | if (value) {
1111 | localStorage.removeItem(key);
1112 | value = JSON.parse(value);
1113 | }
1114 |
1115 | clearExpired();
1116 | return value;
1117 | };
1118 |
1119 | cs.add = function(state) {
1120 | clearExpired();
1121 |
1122 | var key = 'kc-callback-' + state.state;
1123 | state.expires = new Date().getTime() + (60 * 60 * 1000);
1124 | localStorage.setItem(key, JSON.stringify(state));
1125 | };
1126 | };
1127 |
1128 | var CookieStorage = function() {
1129 | if (!(this instanceof CookieStorage)) {
1130 | return new CookieStorage();
1131 | }
1132 |
1133 | var cs = this;
1134 |
1135 | cs.get = function(state) {
1136 | if (!state) {
1137 | return;
1138 | }
1139 |
1140 | var value = getCookie('kc-callback-' + state);
1141 | setCookie('kc-callback-' + state, '', cookieExpiration(-100));
1142 | if (value) {
1143 | return JSON.parse(value);
1144 | }
1145 | };
1146 |
1147 | cs.add = function(state) {
1148 | setCookie('kc-callback-' + state.state, JSON.stringify(state), cookieExpiration(60));
1149 | };
1150 |
1151 | cs.removeItem = function(key) {
1152 | setCookie(key, '', cookieExpiration(-100));
1153 | };
1154 |
1155 | var cookieExpiration = function (minutes) {
1156 | var exp = new Date();
1157 | exp.setTime(exp.getTime() + (minutes*60*1000));
1158 | return exp;
1159 | };
1160 |
1161 | var getCookie = function (key) {
1162 | var name = key + '=';
1163 | var ca = document.cookie.split(';');
1164 | for (var i = 0; i < ca.length; i++) {
1165 | var c = ca[i];
1166 | while (c.charAt(0) == ' ') {
1167 | c = c.substring(1);
1168 | }
1169 | if (c.indexOf(name) == 0) {
1170 | return c.substring(name.length, c.length);
1171 | }
1172 | }
1173 | return '';
1174 | };
1175 |
1176 | var setCookie = function (key, value, expirationDate) {
1177 | var cookie = key + '=' + value + '; '
1178 | + 'expires=' + expirationDate.toUTCString() + '; ';
1179 | document.cookie = cookie;
1180 | };
1181 | };
1182 |
1183 | function createCallbackStorage() {
1184 | try {
1185 | return new LocalStorage();
1186 | } catch (err) {
1187 | }
1188 |
1189 | return new CookieStorage();
1190 | }
1191 |
1192 | var CallbackParser = function(uriToParse, responseMode) {
1193 | if (!(this instanceof CallbackParser)) {
1194 | return new CallbackParser(uriToParse, responseMode);
1195 | }
1196 | var parser = this;
1197 |
1198 | var initialParse = function() {
1199 | var baseUri = null;
1200 | var queryString = null;
1201 | var fragmentString = null;
1202 |
1203 | var questionMarkIndex = uriToParse.indexOf("?");
1204 | var fragmentIndex = uriToParse.indexOf("#", questionMarkIndex + 1);
1205 | if (questionMarkIndex == -1 && fragmentIndex == -1) {
1206 | baseUri = uriToParse;
1207 | } else if (questionMarkIndex != -1) {
1208 | baseUri = uriToParse.substring(0, questionMarkIndex);
1209 | queryString = uriToParse.substring(questionMarkIndex + 1);
1210 | if (fragmentIndex != -1) {
1211 | fragmentIndex = queryString.indexOf("#");
1212 | fragmentString = queryString.substring(fragmentIndex + 1);
1213 | queryString = queryString.substring(0, fragmentIndex);
1214 | }
1215 | } else {
1216 | baseUri = uriToParse.substring(0, fragmentIndex);
1217 | fragmentString = uriToParse.substring(fragmentIndex + 1);
1218 | }
1219 |
1220 | return { baseUri: baseUri, queryString: queryString, fragmentString: fragmentString };
1221 | };
1222 |
1223 | var parseParams = function(paramString) {
1224 | var result = {};
1225 | var params = paramString.split('&');
1226 | for (var i = 0; i < params.length; i++) {
1227 | var p = params[i].split('=');
1228 | var paramName = decodeURIComponent(p[0]);
1229 | var paramValue = decodeURIComponent(p[1]);
1230 | result[paramName] = paramValue;
1231 | }
1232 | return result;
1233 | };
1234 |
1235 | var handleQueryParam = function(paramName, paramValue, oauth) {
1236 | var supportedOAuthParams = [ 'code', 'state', 'error', 'error_description' ];
1237 |
1238 | for (var i = 0 ; i< supportedOAuthParams.length ; i++) {
1239 | if (paramName === supportedOAuthParams[i]) {
1240 | oauth[paramName] = paramValue;
1241 | return true;
1242 | }
1243 | }
1244 | return false;
1245 | };
1246 |
1247 |
1248 | parser.parseUri = function() {
1249 | var parsedUri = initialParse();
1250 |
1251 | var queryParams = {};
1252 | if (parsedUri.queryString) {
1253 | queryParams = parseParams(parsedUri.queryString);
1254 | }
1255 |
1256 | var oauth = { newUrl: parsedUri.baseUri };
1257 | for (var param in queryParams) {
1258 | switch (param) {
1259 | case 'redirect_fragment':
1260 | oauth.fragment = queryParams[param];
1261 | break;
1262 | default:
1263 | if (responseMode != 'query' || !handleQueryParam(param, queryParams[param], oauth)) {
1264 | oauth.newUrl += (oauth.newUrl.indexOf('?') == -1 ? '?' : '&') + param + '=' + encodeURIComponent(queryParams[param]);
1265 | }
1266 | break;
1267 | }
1268 | }
1269 |
1270 | if (responseMode === 'fragment') {
1271 | var fragmentParams = {};
1272 | if (parsedUri.fragmentString) {
1273 | fragmentParams = parseParams(parsedUri.fragmentString);
1274 | }
1275 | for (var param in fragmentParams) {
1276 | oauth[param] = fragmentParams[param];
1277 | }
1278 | }
1279 |
1280 | return oauth;
1281 | };
1282 | };
1283 |
1284 | };
1285 |
1286 | if ( 'object' === "object" && module && 'object' === "object" ) {
1287 | module.exports = Keycloak;
1288 | } else {
1289 | window.Keycloak = Keycloak;
1290 |
1291 | if ( typeof undefined === "function" && undefined.amd ) {
1292 | undefined( "keycloak", [], function () { return Keycloak; } );
1293 | }
1294 | }
1295 | })( window );
1296 | });
1297 |
1298 | /* */
1299 | var installed = false;
1300 |
1301 | var VueKeyCloak = function VueKeyCloak () {};
1302 |
1303 | VueKeyCloak.install = function (Vue, options) {
1304 | if ( options === void 0 ) options = {
1305 | keycloakOptions: {},
1306 | keycloakInitOptions: {},
1307 | refreshTime: 10
1308 | };
1309 |
1310 | if (installed) { return }
1311 | installed = true;
1312 |
1313 | var keycloak$$1 = keycloak(options.keycloakOptions);
1314 |
1315 | var watch = new Vue({
1316 | data: function data () {
1317 | return {
1318 | ready: false,
1319 | authenticated: false,
1320 | user: null,
1321 | token: null,
1322 | resourceAccess: null
1323 | }
1324 | }
1325 | });
1326 |
1327 | keycloak$$1.init(options.keycloakInitOptions).success(function (isAuthenticated) {
1328 | updateWatchVariables(isAuthenticated).then(function () {
1329 | watch.ready = true;
1330 | });
1331 |
1332 | if (isAuthenticated) {
1333 | setInterval(function () {
1334 | keycloak$$1.updateToken(options.refreshTime + 2)
1335 | .success(function (refreshed) {
1336 | if (refreshed) { updateWatchVariables(true); }
1337 | });
1338 | }, options.refreshTime * 1000);
1339 | }
1340 | });
1341 |
1342 | function updateWatchVariables (isAuthenticated) {
1343 | if ( isAuthenticated === void 0 ) isAuthenticated = false;
1344 |
1345 | watch.authenticated = isAuthenticated;
1346 |
1347 | if (isAuthenticated) {
1348 | watch.token = keycloak$$1.token;
1349 | watch.resourceAccess = keycloak$$1.resourceAccess;
1350 | return new Promise(function (resolve, reject) {
1351 | keycloak$$1.loadUserProfile().success(function (user) {
1352 | watch.user = user;
1353 | resolve();
1354 | });
1355 | })
1356 | } else {
1357 | return Promise.resolve()
1358 | }
1359 | }
1360 |
1361 | Object.defineProperty(Vue.prototype, '$keycloak', {
1362 | get: function get () {
1363 | keycloak$$1.ready = watch.ready;
1364 | keycloak$$1.user = watch.user;
1365 | return keycloak$$1
1366 | }
1367 | });
1368 | };
1369 |
1370 | VueKeyCloak.version = '0.0.11';
1371 |
1372 | return VueKeyCloak;
1373 |
1374 | })));
1375 |
--------------------------------------------------------------------------------