@tapni/auth 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/.env.development +5 -0
  2. package/.env.production +8 -0
  3. package/.env.staging +8 -0
  4. package/.env.standalone +4 -0
  5. package/.vscode/extensions.json +3 -0
  6. package/README.md +29 -0
  7. package/index.html +35 -0
  8. package/jsconfig.json +8 -0
  9. package/package.json +46 -0
  10. package/public/.gitkeep +0 -0
  11. package/public/.well-known/apple-app-site-association +11 -0
  12. package/public/.well-known/assetlinks.json +12 -0
  13. package/public/.well-known/microsoft-identity-association.json +7 -0
  14. package/public/android-chrome-192x192.png +0 -0
  15. package/public/android-chrome-512x512.png +0 -0
  16. package/public/apple-touch-icon.png +0 -0
  17. package/public/favicon-16x16.png +0 -0
  18. package/public/favicon-32x32.png +0 -0
  19. package/public/favicon.ico +0 -0
  20. package/public/icon.png +0 -0
  21. package/public/site.webmanifest +1 -0
  22. package/src/App.vue +269 -0
  23. package/src/components/Language.vue +158 -0
  24. package/src/components/LinkIcon.vue +288 -0
  25. package/src/components/ModalOverlay.vue +67 -0
  26. package/src/components/SSO.vue +126 -0
  27. package/src/components/SSOPick.vue +166 -0
  28. package/src/install.js +8 -0
  29. package/src/main.js +96 -0
  30. package/src/mixins/apple.mixin.js +60 -0
  31. package/src/mixins/auth.mixin.js +525 -0
  32. package/src/mixins/facebook.mixin.js +78 -0
  33. package/src/mixins/global.mixin.js +110 -0
  34. package/src/mixins/google.mixin.js +61 -0
  35. package/src/mixins/microsoft.mixin.js +88 -0
  36. package/src/mixins/okta.mixin.js +132 -0
  37. package/src/mixins/qr-auth.mixin.js +112 -0
  38. package/src/mixins/saml.mixin.js +84 -0
  39. package/src/router/index.js +9 -0
  40. package/src/routes.js +55 -0
  41. package/src/services/Api.js +55 -0
  42. package/src/services/AuthService.js +71 -0
  43. package/src/services/CompanyService.js +13 -0
  44. package/src/services/DeviceService.js +10 -0
  45. package/src/services/UserService.js +49 -0
  46. package/src/services/UtilService.js +221 -0
  47. package/src/store/constants.js +8 -0
  48. package/src/store/event-bus.js +30 -0
  49. package/src/store/locales/cn.js +462 -0
  50. package/src/store/locales/de.js +528 -0
  51. package/src/store/locales/en.js +514 -0
  52. package/src/store/locales/es.js +536 -0
  53. package/src/store/locales/fr.js +520 -0
  54. package/src/store/locales/it.js +518 -0
  55. package/src/store/locales/kr.js +496 -0
  56. package/src/store/locales/lang.js +47 -0
  57. package/src/store/locales/sr.js +497 -0
  58. package/src/store/locales/tr.js +491 -0
  59. package/src/styles/framework.css +4012 -0
  60. package/src/styles/inter.ttf +0 -0
  61. package/src/styles/style.css +618 -0
  62. package/src/views/Callback.vue +47 -0
  63. package/src/views/Login.vue +389 -0
  64. package/src/views/QR.vue +39 -0
  65. package/src/views/Register.vue +217 -0
  66. package/src/views/Reset.vue +155 -0
  67. package/src/views/Verify.vue +170 -0
  68. package/src/views/Welcome.vue +69 -0
  69. package/vite.config.js +58 -0
@@ -0,0 +1,78 @@
1
+ import to from 'await-to-js'
2
+ import AuthService from '../services/AuthService'
3
+ import { FacebookLogin } from '@capacitor-community/facebook-login';
4
+ import {EventBus} from "../store/event-bus";
5
+
6
+ window.fbAsyncInit = function () {
7
+ // eslint-disable-next-line no-undef
8
+ FB.init({
9
+ appId: '202577611527680',
10
+ cookie: true, // enable cookies to allow the server to access the session
11
+ xfbml: true, // parse social plugins on this page
12
+ version: 'v16.0' // use graph api current version
13
+ })
14
+ };
15
+
16
+ // Load the SDK asynchronously
17
+ (function (d, s, id) {
18
+ // eslint-disable-next-line one-var
19
+ var js, fjs = d.getElementsByTagName(s)[0]
20
+ if (d.getElementById(id)) return
21
+ js = d.createElement(s); js.id = id
22
+ js.src = 'https://connect.facebook.net/en_US/sdk.js'
23
+ fjs.parentNode.insertBefore(js, fjs)
24
+ }(document, 'script', 'facebook-jssdk'))
25
+
26
+ export default {
27
+ data () {
28
+ return {
29
+ facebookLoad: false
30
+ }
31
+ },
32
+ computed: {
33
+ fbLoginUrl () {
34
+ return 'https://www.facebook.com/v16.0/dialog/oauth?client_id=202577611527680&state={"oauth":"facebook"}&response_type=code%20granted_scopes&scope=email&redirect_uri=https://' + window.location.host + '/login'
35
+ },
36
+ displayFacebookLogin () {
37
+ return false // (this.ssoCompany?.login?.facebook_login && !this.isModal) ?? true;
38
+ }
39
+ },
40
+ methods: {
41
+ async facebookLogin () {
42
+ this.facebookLoad = true
43
+ const FACEBOOK_PERMISSIONS = ['email']
44
+ const [errAuth, user] = await to(FacebookLogin.login({permissions: FACEBOOK_PERMISSIONS}))
45
+ if (errAuth) return this.facebookLoad = false
46
+
47
+ // Track Referrals
48
+ if (this.referral) user.ref = this.referral;
49
+
50
+ if (this.display === 'popup') user.response_type = 'code';
51
+
52
+ if (user.accessToken) {
53
+
54
+ const [err, response] = await to(AuthService.facebookSDK(user, this.storage))
55
+ if (err) {
56
+ this.facebookLoad = false
57
+ EventBus.$emit('ssoEvent', {name: 'setLoading', data: false})
58
+ return this.errorHandler(err)
59
+ }
60
+ if (response.data.success) {
61
+ if (this.display === 'popup') {
62
+ return window.parent?.postMessage({ code: response.data.auth_code, state: this.$route.query.state }, '*');
63
+ }
64
+ await this.loginSetup(response)
65
+ this.getLoggedInAccounts()
66
+ this.$router.push('/' + response.data.data.username + '#edit')
67
+ setTimeout(() => {
68
+ this.facebookLoad = false
69
+ EventBus.$emit('ssoEvent', {name: 'setLoading', data: false})
70
+ }, 1000)
71
+ } else this.errorSnack(this.ssoLang[this.appLang].unexpected_err)
72
+ } else {
73
+ this.facebookLoad = false
74
+ EventBus.$emit('ssoEvent', {name: 'setLoading', data: false})
75
+ }
76
+ }
77
+ }
78
+ }
@@ -0,0 +1,110 @@
1
+ import {Capacitor} from '@capacitor/core';
2
+
3
+ export default {
4
+ computed: {
5
+ home() {
6
+ if (this.storage.token && this.storage.username) {
7
+ if (import.meta.env.VITE_APP_MODE === 'npm') return '/' + this.storage.username
8
+ if (import.meta.env.VITE_APP_MODE === 'redirect') return 'https://t.link/'
9
+ } else return '/welcome'
10
+ },
11
+ isAndroid(){
12
+ return /Android/i.test(navigator.userAgent) || Capacitor.getPlatform() === 'android'
13
+ },
14
+ isiOS() {
15
+ return [
16
+ 'iPad Simulator',
17
+ 'iPhone Simulator',
18
+ 'iPod Simulator',
19
+ 'iPad',
20
+ 'iPhone',
21
+ 'iPod'
22
+ ].includes(navigator.platform)
23
+ // iPad on iOisNativeS 13 detection
24
+ || (navigator.userAgent.includes("Mac") && "ontouchend" in document)
25
+ || Capacitor.getPlatform() === 'ios'
26
+ },
27
+ isNative() {
28
+ return Capacitor.isNativePlatform()
29
+ },
30
+ },
31
+ methods: {
32
+ errorHandler(error) {
33
+ if (error && error.response && error.response.data && error.response.data.error) {
34
+ if (
35
+ error.response.data.error === 'ACCESS_DENIED' ||
36
+ error.response.data.error === 'TOKEN_EXPIRED'
37
+ ) {
38
+ this.logout();
39
+ }
40
+ // Link click network error bug fix
41
+ if (!error.response.data.error.includes('Network Error') && !error.response.data.error.includes('Cannot read properties')) {
42
+ this.errorSnack(error.response.data.error)
43
+ }
44
+ }
45
+ return error
46
+ },
47
+ errorSnack(message) {
48
+ let snackbar = document.getElementById('snackbar')
49
+ let errMessage = document.getElementById('errorMessage')
50
+ let errorSnack = document.getElementById('errorSnack')
51
+ errMessage.innerHTML = message
52
+ snackbar.classList.add('show-snack')
53
+ errorSnack.classList.add('active-snack')
54
+ setTimeout(function () { errorSnack.classList.remove('active-snack'); snackbar.classList.remove('show-snack') }, 3000)
55
+ },
56
+ successSnack(message) {
57
+ let snackbar = document.getElementById('snackbar')
58
+ let successMessage = document.getElementById('successMessage')
59
+ let successSnack = document.getElementById('successSnack')
60
+ successMessage.innerHTML = message
61
+ snackbar.classList.add('show-snack')
62
+ successSnack.classList.add('active-snack')
63
+ setTimeout(function () { successSnack.classList.remove('active-snack'); snackbar.classList.remove('show-snack') }, 3000)
64
+ },
65
+ closeSnacks() {
66
+ document.getElementById('snackbar').classList.remove('show-snack')
67
+ document.getElementById('successSnack').classList.remove('active-snack')
68
+ document.getElementById('errorSnack').classList.remove('active-snack')
69
+ },
70
+ tapsEmoji(count) {
71
+ if (count < 10) return ''
72
+ else if (count <= 50) return '🔥'
73
+ else if (count <= 100) return '💥'
74
+ else if (count <= 300) return '💎'
75
+ else if (count > 300) return '🚀'
76
+ return ''
77
+ },
78
+ /** Function which checks if provided object is empty */
79
+ isEmpty(obj) {
80
+ if (!obj) return true
81
+ for (let prop in obj) {
82
+ if (obj.hasOwnProperty(prop)) {
83
+ return false
84
+ }
85
+ }
86
+ return JSON.stringify(obj) === JSON.stringify({})
87
+ },
88
+ /** Copy string to clipboard */
89
+ copy(str = '') {
90
+ const el = document.createElement('textarea')
91
+ el.value = str
92
+ el.setAttribute('readonly', '')
93
+ el.style.position = 'absolute'
94
+ el.style.left = '-9999px'
95
+ document.body.appendChild(el)
96
+ el.select()
97
+ document.execCommand('copy')
98
+ document.body.removeChild(el)
99
+ this.successSnack(this.ssoLang[this.appLang].copied)
100
+ },
101
+ async getCaptchaToken(action){
102
+ try {
103
+ return await window.grecaptcha.execute(import.meta.env.VITE_APP_GOOGLE_RECAPTCHA_SITE_KEY, {action: action});
104
+ } catch (err){
105
+ console.log(err)
106
+ return null
107
+ }
108
+ },
109
+ }
110
+ }
@@ -0,0 +1,61 @@
1
+ import to from 'await-to-js'
2
+ import AuthService from '../services/AuthService'
3
+ import { GoogleAuth } from '@codetrix-studio/capacitor-google-auth'
4
+ import {EventBus} from "../store/event-bus";
5
+
6
+ export default {
7
+ data () {
8
+ return {
9
+ googleLoad: false
10
+ }
11
+ },
12
+ computed: {
13
+ displayGoogleLogin () {
14
+ return (this.ssoCompany?.login?.google_login && !this.isModal) ?? true;
15
+ }
16
+ },
17
+ mounted () {
18
+ GoogleAuth.initialize()
19
+ },
20
+ methods: {
21
+ async googleLogin () {
22
+
23
+ this.googleLoad = true
24
+ let [errAuth, user] = await to(GoogleAuth.signIn())
25
+ if (errAuth) return this.googleLoad = false
26
+
27
+ // Track Referrals
28
+ if (this.referral) user.ref = this.referral;
29
+
30
+ if (user.authentication && (user.authentication.accessToken || user.authentication.idToken)) {
31
+ // Track Referrals
32
+ if (this.referral) user.ref = this.referral;
33
+
34
+ // Code Login
35
+ if (this.display === 'popup') user.response_type = 'code';
36
+
37
+ const [err, response] = await to(AuthService.googleSDK(user, this.storage))
38
+ if (err) {
39
+ this.googleLoad = false
40
+ EventBus.$emit('ssoEvent', {name: 'setLoading', data: false})
41
+ return this.errorHandler(err)
42
+ }
43
+ if (response.data.success) {
44
+ if (this.display === 'popup') {
45
+ return window.parent?.postMessage({ code: response.data.auth_code, state: this.$route.query.state }, '*');
46
+ }
47
+ await this.loginSetup(response)
48
+ this.getLoggedInAccounts()
49
+ this.$router.push('/' + response.data.data.username + '#edit')
50
+ setTimeout(() => {
51
+ this.googleLoad = false
52
+ EventBus.$emit('ssoEvent', {name: 'setLoading', data: false})
53
+ }, 1000)
54
+ } else this.errorSnack(this.ssoLang[this.appLang].unexpected_err)
55
+ } else {
56
+ this.googleLoad = false
57
+ EventBus.$emit('ssoEvent', {name: 'setLoading', data: false})
58
+ }
59
+ }
60
+ }
61
+ }
@@ -0,0 +1,88 @@
1
+ import to from "await-to-js";
2
+ import AuthService from "../services/AuthService";
3
+ import { MsAuthPlugin } from "@recognizebv/capacitor-plugin-msauth";
4
+ import { EventBus } from "../store/event-bus";
5
+
6
+ export default {
7
+ data() {
8
+ return {
9
+ microsoftLoad: false,
10
+ microsoftSSOLoad: false,
11
+ };
12
+ },
13
+ computed: {
14
+ displayMicrosoftSSOLogin() {
15
+ return (
16
+ (this.ssoCompany?.login?.microsoft_login && !this.isModal) ?? false
17
+ );
18
+ },
19
+ },
20
+ methods: {
21
+ async microsoftLogin(type, sso = {}) {
22
+ if (type === "sso") this.microsoftSSOLoad = true;
23
+ else this.microsoftLoad = true;
24
+
25
+ if (!Object.keys(sso).length) sso = this.ssoCompany.sso?.azure?.sso || {};
26
+
27
+ const [errAuth, user] = await to(
28
+ MsAuthPlugin.login({
29
+ clientId: type === "sso" ? sso.clientID : import.meta.env.VITE_APP_SSO_ID,
30
+ tenant: type === "sso" ? sso.tenant : "consumers",
31
+ scopes: ["User.Read"],
32
+ knownAuthorities: [],
33
+ keyHash: "4+5wCp8QcLptlO0aeP5RDTTOWyg=", // Android,
34
+ redirectUri:
35
+ import.meta.env.NODE_ENV === "development"
36
+ ? `${window.location.origin}/login`
37
+ : "https://" + window.location.host + "/login",
38
+ })
39
+ );
40
+ if (errAuth) {
41
+ console.log("Error: " + JSON.stringify(errAuth));
42
+ return (this.microsoftLoad = false);
43
+ }
44
+
45
+ // Track Referrals
46
+ if (this.referral) user.ref = this.referral;
47
+
48
+ // Code Login
49
+ // if (this.display === "popup") user.response_type = "code";
50
+ user.response_type = "code";
51
+
52
+ if (user && (user.accessToken || user.idToken)) {
53
+ const [err, response] = await to(
54
+ AuthService.microsoftSDK(user, this.storage)
55
+ );
56
+ if (err) {
57
+ this.microsoftLoad = false;
58
+ this.microsoftSSOLoad = false;
59
+ EventBus.$emit("ssoEvent", { name: "setLoading", data: false });
60
+ return this.errorHandler(err);
61
+ }
62
+ if (response.data.success) {
63
+ if (this.display === "popup") {
64
+ return window.parent?.postMessage(
65
+ { code: response.data.auth_code, state: this.$route.query.state },
66
+ "*"
67
+ );
68
+ }
69
+ await this.loginSetup(response);
70
+ this.getLoggedInAccounts();
71
+ this.$router.push("/" + response.data.data.username + "#edit");
72
+ setTimeout(() => {
73
+ this.microsoftLoad = false;
74
+ this.microsoftSSOLoad = false;
75
+ EventBus.$emit("ssoEvent", { name: "setLoading", data: false });
76
+ }, 1000);
77
+ } else this.errorSnack(this.ssoLang[this.appLang].unexpected_err);
78
+ } else {
79
+ this.microsoftLoad = false;
80
+ this.microsoftSSOLoad = false;
81
+ EventBus.$emit("ssoEvent", { name: "setLoading", data: false });
82
+ }
83
+ },
84
+ ssoLogin() {
85
+ EventBus.$emit("toggleSSOModal");
86
+ },
87
+ },
88
+ };
@@ -0,0 +1,132 @@
1
+ import to from 'await-to-js'
2
+ import AuthService from '../services/AuthService'
3
+ import UtilService from '../services/UtilService'
4
+ import { EventBus } from "../store/event-bus";
5
+ import { Capacitor } from '@capacitor/core';
6
+ import axios from 'axios';
7
+ import { Browser } from '@capacitor/browser';
8
+
9
+ // this tutorial was used for plain okta javascript implementation
10
+ // https://developer.okta.com/blog/2019/05/01/is-the-oauth-implicit-flow-dead
11
+ export default {
12
+ data () {
13
+ return {
14
+ oktaAuth: null,
15
+ }
16
+ },
17
+ computed: {
18
+ },
19
+ watch: {
20
+ /*
21
+ '$route.path': async function(routePath) {
22
+ await this.handleOktaRedirect(routePath)
23
+ }
24
+ */
25
+ },
26
+ methods: {
27
+ async exchangeCode (data) {
28
+ let [err, response] = await to(axios.post(data.domain + '/v1/token', new URLSearchParams({code: data.code, client_id: data.clientID, grant_type: "authorization_code", redirect_uri: this.isNative ? 'tapni://t.link/callback/okta' : (location.origin + '/callback/okta'), code_verifier: localStorage.getItem("pkce_code_verifier")})));
29
+ if (err) return this.errorHandler(err);
30
+ localStorage.removeItem("pkce_code_verifier");
31
+
32
+ let user = {accessToken: response.data?.access_token, domain: data.domain}
33
+ if (this.display === 'popup') user.response_type = 'code';
34
+
35
+ [err, response] = await to(AuthService.oktaSDK(user, this.storage));
36
+ if (err) {
37
+ this.oktaLoad = false
38
+ EventBus.$emit('ssoEvent', {name: 'setLoading', data: false})
39
+ return this.errorHandler(err)
40
+ }
41
+ if (response.data.success) {
42
+ if (this.display === 'popup') {
43
+ return window.parent?.postMessage({ code: response.data.auth_code, state: this.$route.query.state }, '*');
44
+ }
45
+ await this.loginSetup(response)
46
+ this.getLoggedInAccounts()
47
+ this.$router.push('/' + response.data.data.username + '#edit')
48
+ setTimeout(() => {
49
+ this.appleLoad = false
50
+ EventBus.$emit('ssoEvent', {name: 'setLoading', data: false})
51
+ }, 1000)
52
+ } else this.errorSnack(this.ssoLang[this.appLang].unexpected_err)
53
+ },
54
+ async oktaLogin (data) {
55
+ const platform = Capacitor.getPlatform() || 'web';
56
+ //const state = btoa(`email=${email}&platform=${platform}`);
57
+ //const state = btoa(`platform=${platform}`);
58
+
59
+ // Create and store a random "state" value
60
+ //localStorage.setItem("pkce_state", state);
61
+ let state = btoa("domain="+data.domain
62
+ + "&client_id="+data.clientID
63
+ + "&platform="+platform
64
+ + "&rand="+UtilService.generateRandomString(28));
65
+
66
+ // Create and store a new PKCE code_verifier (the plaintext random secret)
67
+ let code_verifier = UtilService.generateRandomString(28);
68
+ localStorage.setItem("pkce_code_verifier", code_verifier);
69
+
70
+ // Hash and base64-urlencode the secret to use as the challenge
71
+ let code_challenge = await UtilService.pkceChallengeFromVerifier(code_verifier);
72
+
73
+ let authorization_endpoint = data.domain + '/v1/authorize';
74
+
75
+ let redirectURI;
76
+ if (this.isNative) redirectURI = 'tapni://t.link/callback/okta';
77
+ else redirectURI = location.origin + '/callback/okta';
78
+
79
+ let url = authorization_endpoint
80
+ + "?response_type=code"
81
+ + "&client_id="+encodeURIComponent(data.clientID)
82
+ + "&state="+encodeURIComponent(state)
83
+ + "&scope="+encodeURIComponent('openid email profile')
84
+ + "&redirect_uri="+encodeURIComponent(redirectURI)
85
+ + "&code_challenge="+encodeURIComponent(code_challenge)
86
+ + "&code_challenge_method=S256";
87
+
88
+ let self = this;
89
+ //if (response && response.data && response.data.url) {
90
+ let code;
91
+ window.addEventListener("message", async (message)=> {
92
+ if (!this.allowedOrigins.includes(message.origin)) return console.log('Origin is not allowed!');
93
+ if(message.data.type === 'okta') {
94
+ code = message.data.code;
95
+ await self.exchangeCode({domain: data.domain, code, clientID: data.clientID});
96
+ }
97
+ }, { once: true });
98
+
99
+ let popupWindow;
100
+ if(this.isNative) {
101
+ popupWindow = await Browser.open({ url, presentationStyle: 'popover'});
102
+ } else popupWindow = window.open(url, 'popup','width=600,height=600')
103
+ },
104
+ async handleOktaRedirect() {
105
+ let code = this.$route.query.code;
106
+ let state;
107
+ let clientID;
108
+ let domain;
109
+ let platform;
110
+ let postMessageData = {type: 'okta'}
111
+ if (code) postMessageData.code = code;
112
+ if (this.$route.query.state) {
113
+ state = this.$route.query.state;
114
+ state = atob(state);
115
+ const searchParams = new URLSearchParams(state);
116
+ clientID = searchParams.get('client_id');
117
+ domain = searchParams.get('domain');
118
+ platform = searchParams.get('platform');
119
+ }
120
+ if(window.opener) {
121
+ window.opener.postMessage(postMessageData, location.origin);
122
+ window.close()
123
+ } else {
124
+ if(this.isNative && this.isIOS) await Browser.close();
125
+ EventBus.$emit('ssoEvent', {name: 'setLoading', data: true})
126
+ if (code && clientID && domain) await this.exchangeCode({code, clientID, domain, platform});
127
+ localStorage.removeItem("pkce_code_verifier");
128
+ EventBus.$emit('ssoEvent', {name: 'setLoading', data: false})
129
+ }
130
+ }
131
+ }
132
+ }
@@ -0,0 +1,112 @@
1
+ import to from 'await-to-js'
2
+ import CompanyService from '../services/CompanyService'
3
+ import {nextTick} from "vue";
4
+ import QRCodeStyling from "qr-code-styling";
5
+
6
+ export default {
7
+ data () {
8
+ return {
9
+ isQrCodeLogin: false,
10
+ qrCode: null,
11
+ qrCodeHash: null,
12
+ qrCodeRefreshInterval: null,
13
+ poolingInterval: null,
14
+ }
15
+ },
16
+ props: {
17
+ isModal: {
18
+ type: Boolean,
19
+ required: false,
20
+ default: false,
21
+ },
22
+ },
23
+ mounted () {
24
+ if (this.$route.name === 'AuthQR') this.changeLoginToQr();
25
+ },
26
+ methods: {
27
+ async changeLoginToQr() {
28
+ this.isQrCodeLogin = true;
29
+ await nextTick();
30
+ await this.initQrCodeLogin();
31
+ },
32
+ generateRandomHash() {
33
+ const characters =
34
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
35
+ const charactersLength = characters.length;
36
+ let result = "";
37
+
38
+ // Create an array of 32-bit unsigned integers
39
+ const randomValues = new Uint32Array(32);
40
+
41
+ // Generate random values
42
+ window.crypto.getRandomValues(randomValues);
43
+ randomValues.forEach((value) => {
44
+ result += characters.charAt(value % charactersLength);
45
+ });
46
+
47
+ return result;
48
+ },
49
+ refreshQrCode() {
50
+ if (this.qrCode) {
51
+ document.getElementById("qrCodeContainer")?.childNodes[0]?.remove();
52
+ }
53
+
54
+ this.qrCodeHash = this.generateRandomHash();
55
+
56
+ this.qrCode = new QRCodeStyling({
57
+ width: 300,
58
+ height: 300,
59
+ type: "png",
60
+ image: "",
61
+ imageOptions: {
62
+ margin: 15,
63
+ },
64
+ data: this.qrCodeHash,
65
+ dotsOptions: {
66
+ type: "extra-rounded",
67
+ color: "#000000",
68
+ },
69
+ cornersSquareOptions: {
70
+ type: "extra-rounded",
71
+ color: "#000000",
72
+ },
73
+ cornersDotOptions: {
74
+ type: "",
75
+ color: "#000000",
76
+ },
77
+ backgroundOptions: {
78
+ color: "#ffffff00",
79
+ },
80
+ });
81
+
82
+ this.qrCode.append(document.getElementById("qrCodeContainer"));
83
+ },
84
+ async startQrCodePooling() {
85
+ this.poolingInterval = setInterval(async () => {
86
+ const [err, response] = await to(CompanyService.qrCodePooling({
87
+ qrToken: this.qrCodeHash,
88
+ }, this.storage));
89
+ if (err) return this.errorHandler(err);
90
+ if (response.data.auth_code) {
91
+ if (this.display === 'npm') {
92
+ this.loginSetup({ ...response, isModal: this.isModal });
93
+ this.getLoggedInAccounts();
94
+ }
95
+
96
+ this.loginSuccess({ ...response, isModal: this.isModal });
97
+ clearInterval(this.poolingInterval);
98
+ clearInterval(this.qrCodeRefreshInterval);
99
+ }
100
+ }, 2000);
101
+ },
102
+
103
+ async initQrCodeLogin() {
104
+ this.refreshQrCode();
105
+ await nextTick();
106
+ await this.startQrCodePooling();
107
+ this.qrCodeRefreshInterval = setInterval(() => {
108
+ this.refreshQrCode();
109
+ }, 60000);
110
+ },
111
+ }
112
+ }
@@ -0,0 +1,84 @@
1
+ import { Browser } from '@capacitor/browser';
2
+ import UtilService from '@/services/UtilService';
3
+ import { Capacitor } from "@capacitor/core";
4
+
5
+ export default {
6
+ data () {
7
+ return {
8
+ code_verifier: '',
9
+ code_challenge: ''
10
+ }
11
+ },
12
+ computed: {
13
+ },
14
+ watch: {
15
+ /*
16
+ '$route.path': async function(routePath) {
17
+ await this.handleSamlRedirect(routePath)
18
+ }
19
+ */
20
+ },
21
+ methods: {
22
+ async samlLogin (loginUrl) {
23
+
24
+ // Create and store a new PKCE code_verifier (the plaintext random secret)
25
+ this.code_verifier = UtilService.generateRandomString(28);
26
+ localStorage.setItem("pkce_code_verifier", this.code_verifier);
27
+
28
+ // Hash and base64-urlencode the secret to use as the challenge
29
+ this.code_challenge = await UtilService.pkceChallengeFromVerifier(this.code_verifier);
30
+
31
+ const platform = Capacitor.getPlatform();
32
+
33
+ // append public key as relayState
34
+ let relayState = Buffer.from(`code_challenge=${this.code_challenge}&platform=${platform}&redirect_uri=${(location.origin + '/callback/saml')}&realm=${this.storage.realm}`).toString('base64');
35
+
36
+ loginUrl = `${loginUrl}&RelayState=${relayState}`
37
+
38
+ let self = this;
39
+
40
+ window.addEventListener("message", async (message)=> {
41
+ if (!this.allowedOrigins.includes(message.origin)) return console.log('Origin is not allowed!');
42
+ if(message.data.type === 'saml' && message.data.code) {
43
+ if (this.display === 'popup') {
44
+ return window.parent?.postMessage({ code: message.data.code, state: this.$route.query.state, code_verifier: localStorage.getItem("pkce_code_verifier") }, '*');
45
+ }
46
+ await self.exchangeAuthCode({code: message.data.code, code_verifier: localStorage.getItem("pkce_code_verifier")});
47
+ localStorage.removeItem("pkce_code_verifier");
48
+ }
49
+ }, { once: true });
50
+
51
+ let popupWindow;
52
+ if(this.isNative) {
53
+ popupWindow = await Browser.open({ url: loginUrl, presentationStyle: 'popover'});
54
+ }
55
+ else popupWindow = window.open(loginUrl, 'popup','width=600,height=600')
56
+ /*
57
+ popupWindow.addEventListener('beforeunload', () => {
58
+ console.log('window closed')
59
+ })
60
+ */
61
+ },
62
+ async handleSamlRedirect() {
63
+ let code;
64
+ let postMessageData = {type: 'saml'}
65
+ if (this.$route.query.code) postMessageData.code = this.$route.query.code;
66
+ if(window.opener) {
67
+ window.opener.postMessage(postMessageData, location.origin);
68
+ window.close()
69
+ } else {
70
+ if(this.isNative && this.isIOS) await Browser.close();
71
+ EventBus.$emit('ssoEvent', {name: 'setLoading', data: true})
72
+ if (this.$route.query.code) code = this.$route.query.code
73
+ if (code) {
74
+ if (this.display === 'popup') {
75
+ return window.parent?.postMessage({ code: code, state: this.$route.query.state }, '*');
76
+ }
77
+ await this.exchangeAuthCode({code, code_verifier: localStorage.getItem("pkce_code_verifier")});
78
+ }
79
+ localStorage.removeItem("pkce_code_verifier");
80
+ EventBus.$emit('ssoEvent', {name: 'setLoading', data: false})
81
+ }
82
+ }
83
+ },
84
+ }
@@ -0,0 +1,9 @@
1
+ import { createRouter, createWebHistory } from 'vue-router'
2
+ import routes from '../routes.js'
3
+
4
+ const router = createRouter({
5
+ history: createWebHistory(import.meta.env.BASE_URL),
6
+ routes
7
+ })
8
+
9
+ export default router