@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.
- package/.env.development +5 -0
- package/.env.production +8 -0
- package/.env.staging +8 -0
- package/.env.standalone +4 -0
- package/.vscode/extensions.json +3 -0
- package/README.md +29 -0
- package/index.html +35 -0
- package/jsconfig.json +8 -0
- package/package.json +46 -0
- package/public/.gitkeep +0 -0
- package/public/.well-known/apple-app-site-association +11 -0
- package/public/.well-known/assetlinks.json +12 -0
- package/public/.well-known/microsoft-identity-association.json +7 -0
- package/public/android-chrome-192x192.png +0 -0
- package/public/android-chrome-512x512.png +0 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/favicon-16x16.png +0 -0
- package/public/favicon-32x32.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/icon.png +0 -0
- package/public/site.webmanifest +1 -0
- package/src/App.vue +269 -0
- package/src/components/Language.vue +158 -0
- package/src/components/LinkIcon.vue +288 -0
- package/src/components/ModalOverlay.vue +67 -0
- package/src/components/SSO.vue +126 -0
- package/src/components/SSOPick.vue +166 -0
- package/src/install.js +8 -0
- package/src/main.js +96 -0
- package/src/mixins/apple.mixin.js +60 -0
- package/src/mixins/auth.mixin.js +525 -0
- package/src/mixins/facebook.mixin.js +78 -0
- package/src/mixins/global.mixin.js +110 -0
- package/src/mixins/google.mixin.js +61 -0
- package/src/mixins/microsoft.mixin.js +88 -0
- package/src/mixins/okta.mixin.js +132 -0
- package/src/mixins/qr-auth.mixin.js +112 -0
- package/src/mixins/saml.mixin.js +84 -0
- package/src/router/index.js +9 -0
- package/src/routes.js +55 -0
- package/src/services/Api.js +55 -0
- package/src/services/AuthService.js +71 -0
- package/src/services/CompanyService.js +13 -0
- package/src/services/DeviceService.js +10 -0
- package/src/services/UserService.js +49 -0
- package/src/services/UtilService.js +221 -0
- package/src/store/constants.js +8 -0
- package/src/store/event-bus.js +30 -0
- package/src/store/locales/cn.js +462 -0
- package/src/store/locales/de.js +528 -0
- package/src/store/locales/en.js +514 -0
- package/src/store/locales/es.js +536 -0
- package/src/store/locales/fr.js +520 -0
- package/src/store/locales/it.js +518 -0
- package/src/store/locales/kr.js +496 -0
- package/src/store/locales/lang.js +47 -0
- package/src/store/locales/sr.js +497 -0
- package/src/store/locales/tr.js +491 -0
- package/src/styles/framework.css +4012 -0
- package/src/styles/inter.ttf +0 -0
- package/src/styles/style.css +618 -0
- package/src/views/Callback.vue +47 -0
- package/src/views/Login.vue +389 -0
- package/src/views/QR.vue +39 -0
- package/src/views/Register.vue +217 -0
- package/src/views/Reset.vue +155 -0
- package/src/views/Verify.vue +170 -0
- package/src/views/Welcome.vue +69 -0
- 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
|
+
}
|