@alfabit/keycloak 0.0.40 → 0.0.42
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/package.json +2 -2
- package/src/api/wallet/endpoints/index.ts +30 -3
- package/src/composables/use-keycloak.ts +537 -491
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alfabit/keycloak",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.42",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
7
7
|
"main": "src/index.ts",
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
"keycloak-js": "^26.2.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@alfabit/constants": "^0.0.21",
|
|
24
23
|
"@alfabit/analitics": "^0.0.18",
|
|
24
|
+
"@alfabit/constants": "^0.0.21",
|
|
25
25
|
"@intlify/unplugin-vue-i18n": "^6.0.8",
|
|
26
26
|
"@tanstack/vue-query": "^5.72.0",
|
|
27
27
|
"@types/lodash-es": "^4.17.12",
|
|
@@ -9,11 +9,11 @@ export const postWebLogin = async () => {
|
|
|
9
9
|
const headers: Record<string, string> = {
|
|
10
10
|
"Content-Type": "application/json",
|
|
11
11
|
};
|
|
12
|
-
const token = getToken();
|
|
13
|
-
if(token) headers['Authorization'] = `Bearer ${token}`;
|
|
12
|
+
const token = getToken();
|
|
13
|
+
if (token) headers['Authorization'] = `Bearer ${token}`;
|
|
14
14
|
|
|
15
15
|
let url = `${WALLET_API_HOST}/public/web/login/`;
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
try {
|
|
18
18
|
const analytics = useAnalytics();
|
|
19
19
|
const clientID = await analytics.getClientID();
|
|
@@ -41,4 +41,31 @@ export const postWebLogin = async () => {
|
|
|
41
41
|
const data = await res.json();
|
|
42
42
|
if (referral_token) localStorage.removeItem(REF_KEY);
|
|
43
43
|
return data;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
export const getPublicProfile = async () => {
|
|
48
|
+
if (typeof window === 'undefined') return;
|
|
49
|
+
|
|
50
|
+
const headers: Record<string, string> = {
|
|
51
|
+
"Content-Type": "application/json",
|
|
52
|
+
};
|
|
53
|
+
const token = getToken();
|
|
54
|
+
if (token) headers['Authorization'] = `Bearer ${token}`;
|
|
55
|
+
|
|
56
|
+
let url = `${WALLET_API_HOST}/public/getProfile`;
|
|
57
|
+
|
|
58
|
+
const params: RequestInit = {
|
|
59
|
+
method: "GET",
|
|
60
|
+
headers,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const res = await fetch(url, params);
|
|
64
|
+
|
|
65
|
+
if (!res.ok) {
|
|
66
|
+
throw new Error(`postWebLogin /public/web/login/: HTTP error: ${res.status}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const data = await res.json();
|
|
70
|
+
return data;
|
|
44
71
|
};
|
|
@@ -1,496 +1,542 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
//
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
//
|
|
124
|
-
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
//
|
|
132
|
-
//
|
|
133
|
-
//
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
//
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
//
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
//
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
1
|
+
import Keycloak, { type KeycloakInitOptions } from 'keycloak-js';
|
|
2
|
+
import { inject, reactive, readonly, type Ref, ref } from 'vue';
|
|
3
|
+
import { postWebLogin, getPublicProfile } from '../api/wallet/endpoints';
|
|
4
|
+
import { AppNameEnum, APP_NAME, IS_TELEGRAM_MINI_APP, KEYCLOAK_LOGIN_REQUIRED, OPENID_CLIENT_ID, OPENID_REALM, OPENID_URL, LocationEnum, IS_LOCALHOST } from '@alfabit/constants';
|
|
5
|
+
|
|
6
|
+
export type TIdpHint = 'email' | 'google' | 'apple';
|
|
7
|
+
|
|
8
|
+
enum TariffsGridTypeEnum {
|
|
9
|
+
Deposit = 'deposit',
|
|
10
|
+
Withdraw = 'withdraw',
|
|
11
|
+
Invoice = 'invoice',
|
|
12
|
+
Hedging = 'hedging',
|
|
13
|
+
FiatExchange = 'fiat_exchange',
|
|
14
|
+
InternalTransfer = 'internal_transfer',
|
|
15
|
+
Orders = 'orders',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface TariffGrid {
|
|
19
|
+
tariff_name?: string | null;
|
|
20
|
+
tariff_type?: TariffsGridTypeEnum | null;
|
|
21
|
+
commission?: string | null;
|
|
22
|
+
trading_volume?: number | null;
|
|
23
|
+
is_kyc_required?: boolean | null;
|
|
24
|
+
id?: string;
|
|
25
|
+
created_date?: string;
|
|
26
|
+
creator_id?: number | null;
|
|
27
|
+
modification_date?: string | null;
|
|
28
|
+
modifier_id?: number | null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface ProfileResponse {
|
|
32
|
+
telegram_id?: string | null;
|
|
33
|
+
telegram_username?: string | null;
|
|
34
|
+
email?: string | null;
|
|
35
|
+
created_at: number;
|
|
36
|
+
is_active?: boolean | null;
|
|
37
|
+
balance_usdt?: string | null;
|
|
38
|
+
bonus_balance_usdt?: string | null;
|
|
39
|
+
kyc_status?: string | null;
|
|
40
|
+
kyc_link?: string | null;
|
|
41
|
+
is_wallet_commission?: boolean;
|
|
42
|
+
is_need_withdraw_kyc?: boolean;
|
|
43
|
+
tariff_grid?: TariffGrid | null;
|
|
44
|
+
tariff_grid_current?: TariffGrid | null;
|
|
45
|
+
is_partner_exchange_limit_free?: boolean | null;
|
|
46
|
+
last_login?: string | null;
|
|
47
|
+
is_tariff_autocalculate?: boolean | null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface IUseKeycloak {
|
|
51
|
+
login(email?: string, redirectUri?: string): void;
|
|
52
|
+
loginWithGoogle(redirectUri?: string): void;
|
|
53
|
+
loginWithApple(redirectUri?: string): void;
|
|
54
|
+
loginWithTelegram(redirectUri?: string): void;
|
|
55
|
+
loginPopup(idpHint: TIdpHint, redirectUri?: string): void;
|
|
56
|
+
logout(redirectUri?: string): void;
|
|
57
|
+
register(email?: string, redirectUri?: string): void;
|
|
58
|
+
getToken(): string | undefined;
|
|
59
|
+
isAtExp(): boolean;
|
|
60
|
+
isRtExp(): boolean;
|
|
61
|
+
|
|
62
|
+
readonly isAuth: Ref<boolean>;
|
|
63
|
+
readonly checkAuth: Ref<boolean>;
|
|
64
|
+
readonly userData: Ref<ProfileResponse | null>;
|
|
65
|
+
readonly keycloak: Ref<Keycloak | null>;
|
|
66
|
+
readonly isInitialized: Ref<boolean>;
|
|
67
|
+
readonly keycloakUserData: Ref<Keycloak.KeycloakTokenParsed | null>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const TOKEN_NAME = 'token';
|
|
71
|
+
export const REFRESH_TOKEN_NAME = 'refresh-token';
|
|
72
|
+
export const ID_TOKEN_NAME = 'id-token';
|
|
73
|
+
|
|
74
|
+
const keycloak = ref<Keycloak | null>(null);
|
|
75
|
+
|
|
76
|
+
const isAuth = ref(false);
|
|
77
|
+
const checkAuth = ref(false);
|
|
78
|
+
const userData = ref<ProfileResponse | null>(null);
|
|
79
|
+
const keycloakUserData = ref<Keycloak.KeycloakTokenParsed | null>(null);
|
|
80
|
+
const locale = ref('en');
|
|
81
|
+
|
|
82
|
+
const dontChangeRedirectUri = ref(false);
|
|
83
|
+
const keycloakRedirectUriIsLogout = ref<string | undefined>(undefined);
|
|
84
|
+
|
|
85
|
+
export const setKeycloakLocale = (newLocale: LocationEnum) => {
|
|
86
|
+
locale.value = newLocale;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const setDontChangeRedirectUri = (newDontChangeRedirectUri: boolean) => {
|
|
90
|
+
dontChangeRedirectUri.value = newDontChangeRedirectUri;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export const setKeycloakRedirectUriIsLogout = (newKeycloakRedirectUriIsLogout: string | undefined) => {
|
|
94
|
+
keycloakRedirectUriIsLogout.value = newKeycloakRedirectUriIsLogout;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const isInitialized = ref(false);
|
|
98
|
+
|
|
99
|
+
const getUrl = (): string => {
|
|
100
|
+
const url = new URL(window.location.href);
|
|
101
|
+
const searchParams = new URLSearchParams(window.location.search);
|
|
102
|
+
|
|
103
|
+
if (searchParams.has('redirect_uri')) {
|
|
104
|
+
const redirectUri = searchParams.get('redirect_uri');
|
|
105
|
+
if (!!redirectUri) return redirectUri;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// если в роуте в meta есть параметр keycloakRedirectUriIsLogout - возвращаем его
|
|
109
|
+
if (!!keycloakRedirectUriIsLogout.value && typeof keycloakRedirectUriIsLogout.value === 'string') return `${url.origin}/${locale.value}/${keycloakRedirectUriIsLogout.value}`;
|
|
110
|
+
|
|
111
|
+
// если в $route.meta (который передаем из родительского приложения) есть параметр dontChangeRedirectUri - то из адреса НЕ нужно удалять или добавлять /user
|
|
112
|
+
// если нет - то ничего не делать
|
|
113
|
+
|
|
114
|
+
// if (!dontChangeRedirectUri) {
|
|
115
|
+
|
|
116
|
+
// const path = url.pathname;
|
|
117
|
+
// // Проверяем: путь начинается с /две латинские буквы/ и далее НЕ идёт user/
|
|
118
|
+
// const match = path.match(/^\/([a-zA-Z]{2})(\/(?!user\/))/);
|
|
119
|
+
// if (match) {
|
|
120
|
+
|
|
121
|
+
// console.log('добавляем user')
|
|
122
|
+
|
|
123
|
+
// // Вставляем 'user' после двухбуквенного сегмента
|
|
124
|
+
// url.pathname = path.replace(/^\/([a-zA-Z]{2})\//, '/$1/user/');
|
|
125
|
+
// // Если хочешь обновить текущую страницу:
|
|
126
|
+
// // window.location.href = url.toString();
|
|
127
|
+
// } else {
|
|
128
|
+
|
|
129
|
+
// console.log('удаляем user')
|
|
130
|
+
|
|
131
|
+
// url.pathname = path.replace(/^\/([a-zA-Z]{2})\/user\//, '/$1/');
|
|
132
|
+
// }
|
|
133
|
+
// }
|
|
134
|
+
return url.toString();
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
function isMobile() {
|
|
138
|
+
return /Mobi|Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export const getAuthMethods = () => {
|
|
142
|
+
const methods = reactive({
|
|
143
|
+
async loginPopup(idpHint: TIdpHint, redirectUri?: string) {
|
|
144
|
+
redirectUri = redirectUri ?? getUrl();
|
|
145
|
+
if (isMobile()) {
|
|
146
|
+
switch (idpHint) {
|
|
147
|
+
case 'google':
|
|
148
|
+
methods.loginWithGoogle(redirectUri);
|
|
149
|
+
break;
|
|
150
|
+
case 'apple':
|
|
151
|
+
methods.loginWithApple(redirectUri);
|
|
152
|
+
break;
|
|
153
|
+
default:
|
|
154
|
+
methods.login(undefined, redirectUri);
|
|
155
|
+
}
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
let loginUrl = await keycloak.value?.createLoginUrl({
|
|
160
|
+
idpHint: idpHint === 'email' ? '' : idpHint,
|
|
161
|
+
redirectUri,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
let loginWindow = window.open((loginUrl ?? '') + (idpHint === 'google' ? '&prompt=select_account' : ''), 'Login with Google', 'width=500,height=600');
|
|
165
|
+
|
|
166
|
+
const messageListener = async (event: MessageEvent) => {
|
|
167
|
+
console.log('messageListener', event.data);
|
|
168
|
+
|
|
169
|
+
if (event.data.type === 'keycloak-auth-success' && event.data.message === 'success') {
|
|
170
|
+
window.removeEventListener('message', messageListener);
|
|
171
|
+
//if (!loginWindow || !loginWindow.closed) {
|
|
172
|
+
loginWindow?.close();
|
|
173
|
+
|
|
174
|
+
console.log('window close', {
|
|
175
|
+
loginWindow,
|
|
176
|
+
closed: loginWindow?.closed,
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
//}
|
|
180
|
+
setTimeout(() => {
|
|
181
|
+
window.location.reload();
|
|
182
|
+
console.log('window reload');
|
|
183
|
+
}, 200);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// если в popup пришло сообщение о том, что нужно сменить idpHint, то меняем его и открываем новое окно
|
|
187
|
+
if (event.data.type === 'keycloak-auth-change') {
|
|
188
|
+
loginWindow?.close();
|
|
189
|
+
idpHint = event.data.message as TIdpHint;
|
|
190
|
+
loginUrl = await keycloak.value?.createLoginUrl({
|
|
191
|
+
idpHint,
|
|
192
|
+
redirectUri,
|
|
193
|
+
});
|
|
194
|
+
loginWindow = window.open((loginUrl ?? '') + (idpHint === 'google' ? '&prompt=select_account' : ''), 'Login with Google', 'width=500,height=600');
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
window.addEventListener('message', messageListener);
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
login(email?: string, redirectUri?: string) {
|
|
202
|
+
if (keycloak.value) {
|
|
203
|
+
keycloak.value.login({
|
|
204
|
+
redirectUri: redirectUri ?? getUrl(),
|
|
205
|
+
loginHint: email ?? '',
|
|
206
|
+
locale: locale.value,
|
|
207
|
+
});
|
|
208
|
+
} else {
|
|
209
|
+
console.warn('Keycloak not defined! #login');
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
logout(redirectUri?: string) {
|
|
213
|
+
if (keycloak.value) {
|
|
214
|
+
clearTokens();
|
|
215
|
+
setTimeout(() => {
|
|
216
|
+
keycloak.value &&
|
|
217
|
+
keycloak.value.logout({
|
|
218
|
+
redirectUri: redirectUri ?? getUrl(),
|
|
219
|
+
});
|
|
220
|
+
}, 100);
|
|
221
|
+
} else {
|
|
222
|
+
console.warn('Keycloak not defined! #logout');
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
register(email?: string, redirectUri?: string) {
|
|
226
|
+
if (keycloak.value) {
|
|
227
|
+
keycloak.value.register({
|
|
228
|
+
redirectUri: redirectUri ?? getUrl(),
|
|
229
|
+
loginHint: email ?? '',
|
|
230
|
+
locale: locale.value,
|
|
231
|
+
});
|
|
232
|
+
} else {
|
|
233
|
+
console.warn('Keycloak not defined! #register');
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
loginWithGoogle(redirectUri?: string) {
|
|
237
|
+
if (keycloak.value) {
|
|
238
|
+
keycloak.value
|
|
239
|
+
?.createLoginUrl({
|
|
240
|
+
idpHint: 'google',
|
|
241
|
+
redirectUri: redirectUri ?? getUrl(),
|
|
242
|
+
})
|
|
243
|
+
.then((loginUrl) => (window.location.href = `${loginUrl}&prompt=select_account`));
|
|
244
|
+
} else {
|
|
245
|
+
console.warn('Keycloak not defined! #loginWithGoogle');
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
loginWithApple(redirectUri?: string) {
|
|
249
|
+
if (keycloak.value) {
|
|
250
|
+
keycloak.value.login({
|
|
251
|
+
redirectUri: redirectUri ?? getUrl(),
|
|
252
|
+
idpHint: 'apple',
|
|
253
|
+
locale: locale.value,
|
|
254
|
+
});
|
|
255
|
+
} else {
|
|
256
|
+
console.warn('Keycloak not defined! #loginWithApple');
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
loginWithTelegram(redirectUri?: string) {
|
|
260
|
+
if (keycloak.value) {
|
|
261
|
+
keycloak.value.login({
|
|
262
|
+
redirectUri: redirectUri ?? getUrl(),
|
|
263
|
+
idpHint: 'telegram',
|
|
264
|
+
locale: locale.value,
|
|
265
|
+
});
|
|
266
|
+
} else {
|
|
267
|
+
console.warn('Keycloak not defined! #loginWithTelegram');
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
changePassword(redirectUri?: string) {
|
|
271
|
+
if (keycloak.value) {
|
|
272
|
+
document.cookie = 'from=passport; SameSite=None; Secure';
|
|
273
|
+
void keycloak.value.login({
|
|
274
|
+
action: 'UPDATE_PASSWORD',
|
|
275
|
+
redirectUri: redirectUri ?? getUrl(),
|
|
276
|
+
});
|
|
277
|
+
} else {
|
|
278
|
+
console.warn('Keycloak not defined! #changePassword');
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
configureOtp(redirectUri?: string) {
|
|
282
|
+
if (keycloak.value) {
|
|
283
|
+
document.cookie = 'from=passport; SameSite=None; Secure';
|
|
284
|
+
void keycloak.value.login({
|
|
285
|
+
action: 'CONFIGURE_TOTP',
|
|
286
|
+
redirectUri: redirectUri ?? getUrl(),
|
|
287
|
+
});
|
|
288
|
+
} else {
|
|
289
|
+
console.warn('Keycloak not defined! #configureOtp');
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
return methods;
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
function setTokens() {
|
|
297
|
+
if (!keycloak.value || typeof window === 'undefined') return;
|
|
298
|
+
!!keycloak.value?.token && localStorage.setItem(TOKEN_NAME, keycloak.value.token);
|
|
299
|
+
!!keycloak.value?.refreshToken && localStorage.setItem(REFRESH_TOKEN_NAME, keycloak.value.refreshToken);
|
|
300
|
+
!!keycloak.value?.idToken && localStorage.setItem(ID_TOKEN_NAME, keycloak.value.idToken);
|
|
301
|
+
|
|
302
|
+
!!keycloak.value?.token && sessionStorage.setItem(TOKEN_NAME, keycloak.value.token);
|
|
303
|
+
!!keycloak.value?.refreshToken && sessionStorage.setItem(REFRESH_TOKEN_NAME, keycloak.value.refreshToken);
|
|
304
|
+
!!keycloak.value?.idToken && sessionStorage.setItem(ID_TOKEN_NAME, keycloak.value.idToken);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function clearTokens() {
|
|
308
|
+
if (typeof window === 'undefined') return;
|
|
309
|
+
localStorage.removeItem(TOKEN_NAME);
|
|
310
|
+
localStorage.removeItem(REFRESH_TOKEN_NAME);
|
|
311
|
+
localStorage.removeItem(ID_TOKEN_NAME);
|
|
312
|
+
sessionStorage.removeItem(TOKEN_NAME);
|
|
313
|
+
sessionStorage.removeItem(REFRESH_TOKEN_NAME);
|
|
314
|
+
sessionStorage.removeItem(ID_TOKEN_NAME);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export const getToken = () => (IS_TELEGRAM_MINI_APP && !IS_LOCALHOST ? sessionStorage.getItem(TOKEN_NAME) : localStorage.getItem(TOKEN_NAME)) ?? undefined;
|
|
318
|
+
|
|
319
|
+
const parseJwt = (token: unknown) => {
|
|
320
|
+
if (typeof token !== 'string') throw new Error('Invalid JWT format: #1');
|
|
321
|
+
const [headerB64, payloadB64] = token.split('.');
|
|
322
|
+
if (!headerB64 || !payloadB64) throw new Error('Invalid JWT format: #2');
|
|
323
|
+
const decodeBase64Url = (str: string) => JSON.parse(atob(str.replace(/-/g, '+').replace(/_/g, '/')));
|
|
324
|
+
const header = decodeBase64Url(headerB64);
|
|
325
|
+
const payload = decodeBase64Url(payloadB64);
|
|
326
|
+
const now = Math.floor(Date.now() / 1000);
|
|
327
|
+
const isExpired = payload.exp !== undefined && now > payload.exp;
|
|
328
|
+
return {
|
|
329
|
+
header,
|
|
330
|
+
payload,
|
|
331
|
+
isExpired,
|
|
332
|
+
expiresAt: payload.exp ? new Date(payload.exp * 1000).toISOString() : null,
|
|
333
|
+
};
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
export const isAtExp = () => {
|
|
337
|
+
if (typeof window === 'undefined') return false;
|
|
338
|
+
try {
|
|
339
|
+
return parseJwt(localStorage.getItem(TOKEN_NAME)).isExpired;
|
|
340
|
+
} catch (e) {
|
|
341
|
+
return false;
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
export const isRtExp = () => {
|
|
346
|
+
if (typeof window === 'undefined') return false;
|
|
347
|
+
try {
|
|
348
|
+
// если рт не истек, возвращаем true
|
|
349
|
+
return parseJwt(localStorage.getItem(REFRESH_TOKEN_NAME)).isExpired;
|
|
350
|
+
} catch (e) {
|
|
351
|
+
clearTokens();
|
|
352
|
+
if (e instanceof Error) {
|
|
353
|
+
console.log('checkIsPassportAuth error: ', e.message);
|
|
354
|
+
}
|
|
355
|
+
return false;
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
// const isPopup = () => {
|
|
360
|
+
// console.log('run isPopup');
|
|
361
|
+
// // для авторизации в popup - это запускается в дочернем окне
|
|
303
362
|
// try {
|
|
304
|
-
// //
|
|
305
|
-
//
|
|
306
|
-
//
|
|
307
|
-
|
|
308
|
-
//
|
|
309
|
-
//
|
|
363
|
+
// // Отправляем сообщение родителю
|
|
364
|
+
// if (window.opener) {
|
|
365
|
+
// console.log('window opener');
|
|
366
|
+
|
|
367
|
+
// window.opener.postMessage(
|
|
368
|
+
// {
|
|
369
|
+
// type: 'keycloak-auth-success',
|
|
370
|
+
// message: 'success',
|
|
371
|
+
// },
|
|
372
|
+
// '*'
|
|
373
|
+
// );
|
|
310
374
|
// }
|
|
311
|
-
//
|
|
375
|
+
// } catch (err) {
|
|
376
|
+
// console.warn('Error in login-popup-redirect.html:', err);
|
|
312
377
|
// }
|
|
313
378
|
// };
|
|
314
379
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
//
|
|
326
|
-
//
|
|
327
|
-
//
|
|
328
|
-
//
|
|
329
|
-
|
|
330
|
-
//
|
|
331
|
-
//
|
|
332
|
-
//
|
|
333
|
-
//
|
|
334
|
-
//
|
|
335
|
-
|
|
336
|
-
//
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
//
|
|
347
|
-
//
|
|
348
|
-
// //
|
|
349
|
-
// //
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
//
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
//
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
//
|
|
369
|
-
//
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
//
|
|
391
|
-
|
|
392
|
-
//
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
//
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
// dontChangeRedirectUri ||= inject('dontChangeRedirectUri', ref(false));
|
|
480
|
-
// keycloakRedirectUriIsLogout ||= inject('keycloakRedirectUriIsLogout', ref(undefined));
|
|
481
|
-
|
|
482
|
-
// _keycloakInit = {
|
|
483
|
-
// keycloak: readonly(keycloak) as Ref<Keycloak | null>,
|
|
484
|
-
// isInitialized: readonly(isInitialized),
|
|
485
|
-
// isAuth: readonly(isAuth),
|
|
486
|
-
// checkAuth: readonly(checkAuth),
|
|
487
|
-
|
|
488
|
-
// keycloakUserData,
|
|
489
|
-
|
|
490
|
-
// ...getAuthMethods(),
|
|
491
|
-
// getToken,
|
|
492
|
-
// isAtExp,
|
|
493
|
-
// isRtExp,
|
|
494
|
-
// };
|
|
495
|
-
// return _keycloakInit;
|
|
496
|
-
// };
|
|
380
|
+
let isInitKeycloak = false;
|
|
381
|
+
|
|
382
|
+
export async function initKeycloak() {
|
|
383
|
+
if (!!keycloak.value || typeof window === 'undefined' || isInitKeycloak) return;
|
|
384
|
+
isInitKeycloak = true;
|
|
385
|
+
|
|
386
|
+
if (!OPENID_URL) throw new Error('Не задана переменная keycloak: url');
|
|
387
|
+
if (!OPENID_REALM) throw new Error('Не задана переменная keycloak: realm');
|
|
388
|
+
if (!OPENID_CLIENT_ID) throw new Error('Не задана переменная keycloak: clientId');
|
|
389
|
+
|
|
390
|
+
// console.log({
|
|
391
|
+
// OPENID_URL,
|
|
392
|
+
// OPENID_REALM,
|
|
393
|
+
// OPENID_CLIENT_ID,
|
|
394
|
+
|
|
395
|
+
// VITE_OPENID_URL: import.meta.env?.VITE_OPENID_URL,
|
|
396
|
+
// VITE_PUBLIC_KEYCLOAK_URL: import.meta.env?.VITE_PUBLIC_KEYCLOAK_URL,
|
|
397
|
+
// VITE_OPENID_REALM: import.meta.env?.VITE_OPENID_REALM,
|
|
398
|
+
// VITE_PUBLIC_KEYCLOAK_REALM: import.meta.env?.VITE_PUBLIC_KEYCLOAK_REALM,
|
|
399
|
+
// VITE_OPENID_CLIENT_ID: import.meta.env?.VITE_OPENID_CLIENT_ID,
|
|
400
|
+
// VITE_PUBLIC_KEYCLOAK_CLIENT_ID: import.meta.env?.VITE_PUBLIC_KEYCLOAK_CLIENT_ID,
|
|
401
|
+
// });
|
|
402
|
+
|
|
403
|
+
keycloak.value = new Keycloak({
|
|
404
|
+
url: OPENID_URL,
|
|
405
|
+
realm: OPENID_REALM,
|
|
406
|
+
clientId: OPENID_CLIENT_ID,
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
// keycloak.value && (keycloak.value.onAuthLogout = () => {
|
|
412
|
+
// console.warn('>>>>>>>> Сессия истекла');
|
|
413
|
+
// // например, показать уведомление
|
|
414
|
+
// // или открыть собственное модальное окно логина
|
|
415
|
+
// });
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
const isNeedAuth = !!getToken() && !isRtExp();
|
|
421
|
+
|
|
422
|
+
const initOptions: KeycloakInitOptions = {
|
|
423
|
+
pkceMethod: 'S256',
|
|
424
|
+
checkLoginIframe: false,
|
|
425
|
+
// enableLogging: true,
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
// Устанавливаем onLoad только если нужно
|
|
429
|
+
if (isNeedAuth && (KEYCLOAK_LOGIN_REQUIRED || (APP_NAME !== AppNameEnum.PASSPORT))) {
|
|
430
|
+
initOptions.onLoad = 'login-required';
|
|
431
|
+
} else if (isNeedAuth) {
|
|
432
|
+
initOptions.onLoad = 'check-sso';
|
|
433
|
+
// clearTokens();
|
|
434
|
+
// window.location.href = getUrl() as string;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (!isNeedAuth) {
|
|
438
|
+
clearTokens();
|
|
439
|
+
} else {
|
|
440
|
+
const token = localStorage.getItem(TOKEN_NAME);
|
|
441
|
+
const refreshToken = localStorage.getItem(REFRESH_TOKEN_NAME);
|
|
442
|
+
const idToken = localStorage.getItem(ID_TOKEN_NAME);
|
|
443
|
+
|
|
444
|
+
if (token) initOptions.token = token;
|
|
445
|
+
if (refreshToken) initOptions.refreshToken = refreshToken;
|
|
446
|
+
if (idToken) initOptions.idToken = idToken;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
try {
|
|
450
|
+
const res = await keycloak.value.init(initOptions);
|
|
451
|
+
|
|
452
|
+
setTokens();
|
|
453
|
+
|
|
454
|
+
if (res) {
|
|
455
|
+
//await keycloak.value.updateToken(70);
|
|
456
|
+
setTokens();
|
|
457
|
+
// isPopup();
|
|
458
|
+
keycloakUserData.value = keycloak.value?.tokenParsed ?? null;
|
|
459
|
+
isAuth.value = true;
|
|
460
|
+
try {
|
|
461
|
+
await postWebLogin();
|
|
462
|
+
userData.value = await getPublicProfile();
|
|
463
|
+
} catch (e) { }
|
|
464
|
+
|
|
465
|
+
//setTimeout(() => {
|
|
466
|
+
setInterval(async () => {
|
|
467
|
+
if (!keycloak.value) return;
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
try {
|
|
471
|
+
const res = await keycloak.value.updateToken(70);
|
|
472
|
+
if (res) {
|
|
473
|
+
setTokens();
|
|
474
|
+
keycloakUserData.value = keycloak.value?.tokenParsed ?? null;
|
|
475
|
+
isAuth.value = true;
|
|
476
|
+
try {
|
|
477
|
+
await postWebLogin();
|
|
478
|
+
userData.value = await getPublicProfile();
|
|
479
|
+
} catch { }
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
catch (e) {
|
|
483
|
+
console.warn('Ошибка Keycloak:', e);
|
|
484
|
+
keycloak.value?.clearToken();
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
}, 6000);
|
|
490
|
+
//}, 60000);
|
|
491
|
+
}
|
|
492
|
+
return isAuth.value;
|
|
493
|
+
} catch (error) {
|
|
494
|
+
console.warn('Ошибка Keycloak:', error);
|
|
495
|
+
isAuth.value = false;
|
|
496
|
+
return false;
|
|
497
|
+
} finally {
|
|
498
|
+
checkAuth.value = true;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
interface IKeycliakInit {
|
|
503
|
+
keycloakInit?: Ref<Keycloak | null | undefined> | undefined;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
export async function createKeycloakInit({ keycloakInit }: IKeycliakInit = {}) {
|
|
507
|
+
if (typeof window === 'undefined') return;
|
|
508
|
+
if (!!keycloakInit && keycloakInit?.value) {
|
|
509
|
+
keycloak.value = keycloakInit.value;
|
|
510
|
+
isAuth.value = keycloak.value?.authenticated ?? false;
|
|
511
|
+
} else {
|
|
512
|
+
await initKeycloak();
|
|
513
|
+
}
|
|
514
|
+
isInitialized.value = true;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// ...весь остальной код до сюда не меняется...
|
|
518
|
+
|
|
519
|
+
let _keycloakInit: IUseKeycloak | null = null;
|
|
520
|
+
|
|
521
|
+
export const keycloakInit = (dontChangeRedirectUri?: Ref<boolean>, keycloakRedirectUriIsLogout?: Ref<string | undefined>): IUseKeycloak => {
|
|
522
|
+
if (_keycloakInit) return _keycloakInit;
|
|
523
|
+
|
|
524
|
+
dontChangeRedirectUri ||= inject('dontChangeRedirectUri', ref(false));
|
|
525
|
+
keycloakRedirectUriIsLogout ||= inject('keycloakRedirectUriIsLogout', ref(undefined));
|
|
526
|
+
|
|
527
|
+
_keycloakInit = {
|
|
528
|
+
keycloak: readonly(keycloak) as Ref<Keycloak | null>,
|
|
529
|
+
isInitialized: readonly(isInitialized),
|
|
530
|
+
isAuth: readonly(isAuth),
|
|
531
|
+
checkAuth: readonly(checkAuth),
|
|
532
|
+
userData: readonly(userData),
|
|
533
|
+
|
|
534
|
+
keycloakUserData,
|
|
535
|
+
|
|
536
|
+
...getAuthMethods(),
|
|
537
|
+
getToken,
|
|
538
|
+
isAtExp,
|
|
539
|
+
isRtExp,
|
|
540
|
+
};
|
|
541
|
+
return _keycloakInit;
|
|
542
|
+
};
|