academe-kit 0.6.8 → 0.7.0
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/dist/index.cjs +79 -332
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +0 -8
- package/dist/index.esm.js +80 -333
- package/dist/index.esm.js.map +1 -1
- package/dist/types/context/SecurityProvider/index.d.ts +0 -7
- package/dist/types/context/SecurityProvider/types.d.ts +0 -2
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -5984,37 +5984,20 @@ exports.GLOBAL_ROLES = void 0;
|
|
|
5984
5984
|
GLOBAL_ROLES["GUARDIAN"] = "guardian";
|
|
5985
5985
|
})(exports.GLOBAL_ROLES || (exports.GLOBAL_ROLES = {}));
|
|
5986
5986
|
|
|
5987
|
-
|
|
5988
|
-
if (typeof window === "undefined")
|
|
5989
|
-
return
|
|
5990
|
-
}
|
|
5991
|
-
const
|
|
5992
|
-
if (
|
|
5993
|
-
return
|
|
5994
|
-
}
|
|
5995
|
-
// Validar e decodificar o token mobile
|
|
5996
|
-
try {
|
|
5997
|
-
const decoded = jwtDecode(keycloakConfig.token);
|
|
5998
|
-
if (!decoded) {
|
|
5999
|
-
console.error("[SecurityProvider] Token mobile inválido - ignorando");
|
|
6000
|
-
return { token: undefined, isMobile: false, decoded: null };
|
|
6001
|
-
}
|
|
6002
|
-
// Verificar se o token não está expirado
|
|
6003
|
-
const now = Math.floor(Date.now() / 1000);
|
|
6004
|
-
if (decoded.exp && decoded.exp < now) {
|
|
6005
|
-
console.error("[SecurityProvider] Token mobile expirado - ignorando");
|
|
6006
|
-
return { token: undefined, isMobile: false, decoded: null };
|
|
6007
|
-
}
|
|
6008
|
-
console.log("[SecurityProvider] ✅ Token mobile detectado síncronamente");
|
|
6009
|
-
return { token: keycloakConfig.token, isMobile: true, decoded };
|
|
6010
|
-
}
|
|
6011
|
-
catch (error) {
|
|
6012
|
-
console.error("[SecurityProvider] Erro ao decodificar token mobile:", error);
|
|
6013
|
-
return { token: undefined, isMobile: false, decoded: null };
|
|
5987
|
+
const getCookie = (name) => {
|
|
5988
|
+
if (typeof window === "undefined")
|
|
5989
|
+
return null;
|
|
5990
|
+
const value = `; ${document.cookie}`;
|
|
5991
|
+
const parts = value.split(`; ${name}=`);
|
|
5992
|
+
if (parts.length === 2) {
|
|
5993
|
+
return parts.pop()?.split(";").shift() || null;
|
|
6014
5994
|
}
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
const
|
|
5995
|
+
return null;
|
|
5996
|
+
};
|
|
5997
|
+
const getAccessTokenFromCookies = () => {
|
|
5998
|
+
const token = getCookie("KC_FORCE_AUTH_TOKEN");
|
|
5999
|
+
return token || undefined;
|
|
6000
|
+
};
|
|
6018
6001
|
const AcademeAuthProvider = ({ realm, hubUrl, children, clientId, keycloakUrl, apiBaseUrl, skipApiUserFetch, }) => {
|
|
6019
6002
|
const oidcConfig = {
|
|
6020
6003
|
authority: `${keycloakUrl}/realms/${realm}`,
|
|
@@ -6028,18 +6011,11 @@ const AcademeAuthProvider = ({ realm, hubUrl, children, clientId, keycloakUrl, a
|
|
|
6028
6011
|
},
|
|
6029
6012
|
automaticSilentRenew: true,
|
|
6030
6013
|
};
|
|
6031
|
-
// Se temos token mobile válido, NÃO monta o AuthProvider do OIDC
|
|
6032
|
-
// Isso evita que ele tente fazer signinSilent() e cause erro CSP em iframes
|
|
6033
|
-
if (initialMobileState.isMobile) {
|
|
6034
|
-
console.log("[AcademeAuthProvider] 🖼️ Modo mobile/iframe - usando SecurityProviderMobileOnly");
|
|
6035
|
-
return (jsxRuntime.jsx(SecurityProviderMobileOnly, { apiBaseUrl: apiBaseUrl, skipApiUserFetch: skipApiUserFetch, children: children }));
|
|
6036
|
-
}
|
|
6037
6014
|
return (jsxRuntime.jsx(AuthProvider, { ...oidcConfig, children: jsxRuntime.jsx(SecurityProvider, { hubUrl: hubUrl, apiBaseUrl: apiBaseUrl, skipApiUserFetch: skipApiUserFetch, children: children }) }));
|
|
6038
6015
|
};
|
|
6039
6016
|
const SecurityContext = React2.createContext({
|
|
6040
6017
|
isInitialized: false,
|
|
6041
6018
|
isTokenReady: false,
|
|
6042
|
-
isMobileAuth: false,
|
|
6043
6019
|
user: null,
|
|
6044
6020
|
refreshUserData: async () => { },
|
|
6045
6021
|
signOut: () => null,
|
|
@@ -6068,35 +6044,53 @@ const SecurityProvider = ({ apiBaseUrl = "https://stg-api.academe.com.br", skipA
|
|
|
6068
6044
|
const auth = useAuth();
|
|
6069
6045
|
const [currentUser, setCurrentUser] = React2.useState(null);
|
|
6070
6046
|
const [isRefreshing, setIsRefreshing] = React2.useState(false);
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
const hasTriedSignInSilent = React2.useRef(initialMobileState.isMobile);
|
|
6074
|
-
const [isTokenReady, setIsTokenReady] = React2.useState(initialMobileState.isMobile);
|
|
6075
|
-
// --- Estado para autenticação mobile (inicializado com detecção síncrona) ---
|
|
6076
|
-
const [isMobileAuth, setIsMobileAuth] = React2.useState(initialMobileState.isMobile);
|
|
6077
|
-
const [mobileToken, setMobileToken] = React2.useState(initialMobileState.token);
|
|
6078
|
-
// Ref para armazenar o resolver da Promise de token
|
|
6047
|
+
const hasTriedSignInSilent = React2.useRef(false);
|
|
6048
|
+
const [isTokenReady, setIsTokenReady] = React2.useState(false);
|
|
6079
6049
|
const tokenReadyResolverRef = React2.useRef(null);
|
|
6080
6050
|
const tokenReadyPromiseRef = React2.useRef(null);
|
|
6081
|
-
|
|
6082
|
-
|
|
6051
|
+
const [accessToken, setAccessToken] = React2.useState(() => {
|
|
6052
|
+
const cookieToken = getAccessTokenFromCookies();
|
|
6053
|
+
return cookieToken || auth.user?.access_token;
|
|
6054
|
+
});
|
|
6055
|
+
const decodedAccessToken = React2.useMemo(() => {
|
|
6056
|
+
return accessToken ? decodeAccessToken(accessToken) : null;
|
|
6057
|
+
}, [accessToken]);
|
|
6058
|
+
const userProfileSub = auth.user?.profile?.sub || decodedAccessToken?.sub;
|
|
6059
|
+
const isAuthenticated = auth.isAuthenticated || !!accessToken;
|
|
6083
6060
|
const isLoading = auth.isLoading;
|
|
6084
6061
|
const activeNavigator = auth.activeNavigator;
|
|
6085
|
-
const accessToken = auth.user?.access_token;
|
|
6086
6062
|
const userProfile = auth.user?.profile;
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
//
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6063
|
+
console.log(">> isAuthenticated:", isAuthenticated);
|
|
6064
|
+
console.log(">> accessToken:", accessToken);
|
|
6065
|
+
const currentTokenRef = React2.useRef(undefined);
|
|
6066
|
+
// Efeito para sincronizar o token do auth com o state
|
|
6067
|
+
// e também verificar cookies periodicamente
|
|
6068
|
+
React2.useEffect(() => {
|
|
6069
|
+
const cookieToken = getAccessTokenFromCookies();
|
|
6070
|
+
const authToken = auth.user?.access_token;
|
|
6071
|
+
// Prioridade: Cookie > Auth token
|
|
6072
|
+
const newToken = cookieToken || authToken;
|
|
6073
|
+
if (newToken && newToken !== accessToken) {
|
|
6074
|
+
setAccessToken(newToken);
|
|
6075
|
+
}
|
|
6076
|
+
}, [auth.user?.access_token, accessToken]);
|
|
6077
|
+
// Efeito opcional: verificar cookies em intervalos (útil se o cookie pode mudar externamente)
|
|
6078
|
+
React2.useEffect(() => {
|
|
6079
|
+
const checkCookieToken = () => {
|
|
6080
|
+
const cookieToken = getAccessTokenFromCookies();
|
|
6081
|
+
if (cookieToken && cookieToken !== accessToken) {
|
|
6082
|
+
setAccessToken(cookieToken);
|
|
6083
|
+
}
|
|
6084
|
+
};
|
|
6085
|
+
// Verifica a cada 5 segundos (ajuste conforme necessário)
|
|
6086
|
+
const interval = setInterval(checkCookieToken, 5000);
|
|
6087
|
+
return () => clearInterval(interval);
|
|
6088
|
+
}, [accessToken]);
|
|
6089
|
+
// --- 1. Silent Check Inicial (Check SSO) ---
|
|
6096
6090
|
React2.useEffect(() => {
|
|
6097
|
-
//
|
|
6098
|
-
if (
|
|
6099
|
-
|
|
6091
|
+
// Se já temos um token do cookie, não precisamos fazer silent check
|
|
6092
|
+
if (accessToken) {
|
|
6093
|
+
hasTriedSignInSilent.current = true;
|
|
6100
6094
|
return;
|
|
6101
6095
|
}
|
|
6102
6096
|
if (!isAuthenticated &&
|
|
@@ -6109,31 +6103,19 @@ const SecurityProvider = ({ apiBaseUrl = "https://stg-api.academe.com.br", skipA
|
|
|
6109
6103
|
});
|
|
6110
6104
|
}
|
|
6111
6105
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
6112
|
-
}, [isAuthenticated, isLoading, activeNavigator,
|
|
6106
|
+
}, [isAuthenticated, isLoading, activeNavigator, accessToken]);
|
|
6113
6107
|
// --- 2. Configuração de API e Services ---
|
|
6114
6108
|
const apiClient = React2.useMemo(() => {
|
|
6115
6109
|
const client = createAcademeApiClient(apiBaseUrl);
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
tokenReadyPromiseRef.current = Promise.resolve();
|
|
6120
|
-
tokenReadyResolverRef.current = () => { };
|
|
6121
|
-
}
|
|
6122
|
-
else {
|
|
6123
|
-
// Inicializa a Promise de token ready para modo OIDC
|
|
6124
|
-
tokenReadyPromiseRef.current = new Promise((resolve) => {
|
|
6125
|
-
tokenReadyResolverRef.current = resolve;
|
|
6126
|
-
});
|
|
6127
|
-
}
|
|
6128
|
-
// Middleware que aguarda o token estar disponível antes de fazer requests
|
|
6110
|
+
tokenReadyPromiseRef.current = new Promise((resolve) => {
|
|
6111
|
+
tokenReadyResolverRef.current = resolve;
|
|
6112
|
+
});
|
|
6129
6113
|
client.use({
|
|
6130
6114
|
async onRequest({ request }) {
|
|
6131
|
-
// Se já tem token, usa imediatamente
|
|
6132
6115
|
if (currentTokenRef.current) {
|
|
6133
6116
|
request.headers.set("Authorization", `Bearer ${currentTokenRef.current}`);
|
|
6134
6117
|
return request;
|
|
6135
6118
|
}
|
|
6136
|
-
// Se ainda está carregando a auth, aguarda o token ficar pronto
|
|
6137
6119
|
if (tokenReadyPromiseRef.current) {
|
|
6138
6120
|
await tokenReadyPromiseRef.current;
|
|
6139
6121
|
if (currentTokenRef.current) {
|
|
@@ -6148,56 +6130,33 @@ const SecurityProvider = ({ apiBaseUrl = "https://stg-api.academe.com.br", skipA
|
|
|
6148
6130
|
const services = React2.useMemo(() => {
|
|
6149
6131
|
return createAcademeServices(apiClient);
|
|
6150
6132
|
}, [apiClient]);
|
|
6151
|
-
// Token efetivo: prioriza mobile, depois OIDC
|
|
6152
|
-
const effectiveToken = mobileToken || accessToken;
|
|
6153
|
-
const decodedAccessToken = React2.useMemo(() => {
|
|
6154
|
-
return effectiveToken ? decodeAccessToken(effectiveToken) : null;
|
|
6155
|
-
}, [effectiveToken]);
|
|
6156
|
-
// Atualização do Token e resolução da Promise
|
|
6157
|
-
// Não sobrescreve se estamos em modo mobile
|
|
6158
6133
|
React2.useEffect(() => {
|
|
6159
|
-
// Se estamos em modo mobile, o token já foi configurado
|
|
6160
|
-
if (isMobileAuth) {
|
|
6161
|
-
return;
|
|
6162
|
-
}
|
|
6163
6134
|
currentTokenRef.current = accessToken;
|
|
6164
6135
|
if (typeof window !== "undefined") {
|
|
6165
6136
|
window.accessToken = accessToken;
|
|
6166
6137
|
}
|
|
6167
6138
|
if (accessToken) {
|
|
6168
|
-
// Resolve a promise indicando que o token está pronto
|
|
6169
6139
|
if (tokenReadyResolverRef.current) {
|
|
6170
6140
|
tokenReadyResolverRef.current();
|
|
6171
6141
|
setIsTokenReady(true);
|
|
6172
6142
|
}
|
|
6173
6143
|
}
|
|
6174
6144
|
else if (!isLoading && !isAuthenticated) {
|
|
6175
|
-
// Usuário não autenticado - resolve a promise para não bloquear requests públicas
|
|
6176
6145
|
if (tokenReadyResolverRef.current) {
|
|
6177
6146
|
tokenReadyResolverRef.current();
|
|
6178
6147
|
setIsTokenReady(true);
|
|
6179
6148
|
}
|
|
6180
6149
|
}
|
|
6181
|
-
}, [accessToken, isLoading, isAuthenticated
|
|
6182
|
-
// --- 3. Helpers de Usuário e Roles ---
|
|
6150
|
+
}, [accessToken, isLoading, isAuthenticated]);
|
|
6183
6151
|
const getKeycloakUser = React2.useCallback(() => {
|
|
6184
|
-
// Se estamos em modo mobile, extrair do token decodificado
|
|
6185
|
-
if (isMobileAuth && decodedAccessToken) {
|
|
6186
|
-
return {
|
|
6187
|
-
email: decodedAccessToken.email || "",
|
|
6188
|
-
name: decodedAccessToken.given_name || "",
|
|
6189
|
-
lastName: decodedAccessToken.family_name || "",
|
|
6190
|
-
};
|
|
6191
|
-
}
|
|
6192
|
-
// Modo OIDC normal
|
|
6193
6152
|
const profile = userProfile;
|
|
6194
6153
|
return {
|
|
6195
|
-
email: profile?.email || "",
|
|
6196
|
-
name: profile?.given_name || "",
|
|
6197
|
-
lastName: profile?.family_name || "",
|
|
6198
|
-
avatar_url: decodedAccessToken?.avatar_url
|
|
6154
|
+
email: profile?.email || decodedAccessToken?.email || "",
|
|
6155
|
+
name: profile?.given_name || decodedAccessToken?.given_name || "",
|
|
6156
|
+
lastName: profile?.family_name || decodedAccessToken?.family_name || "",
|
|
6157
|
+
avatar_url: decodedAccessToken?.avatar_url,
|
|
6199
6158
|
};
|
|
6200
|
-
}, [userProfile,
|
|
6159
|
+
}, [userProfile, decodedAccessToken]);
|
|
6201
6160
|
const hasRealmRole = React2.useCallback((role) => {
|
|
6202
6161
|
return decodedAccessToken?.realm_access?.roles?.includes(role) ?? false;
|
|
6203
6162
|
}, [decodedAccessToken]);
|
|
@@ -6212,20 +6171,17 @@ const SecurityProvider = ({ apiBaseUrl = "https://stg-api.academe.com.br", skipA
|
|
|
6212
6171
|
}
|
|
6213
6172
|
return Object.values(decodedAccessToken.resource_access).some((resource) => resource.roles?.includes(role));
|
|
6214
6173
|
}, [decodedAccessToken]);
|
|
6215
|
-
// Autenticação efetiva: mobile ou OIDC
|
|
6216
|
-
const effectiveIsAuthenticated = isMobileAuth || isAuthenticated;
|
|
6217
|
-
const effectiveUserSub = isMobileAuth ? decodedAccessToken?.sub : userProfileSub;
|
|
6218
6174
|
// --- 4. Fetch de Dados do Usuário (Backend) ---
|
|
6219
6175
|
React2.useEffect(() => {
|
|
6220
6176
|
let isMounted = true;
|
|
6221
6177
|
const fetchUserData = async () => {
|
|
6222
|
-
if (
|
|
6178
|
+
if (isAuthenticated) {
|
|
6223
6179
|
if (skipApiUserFetch) {
|
|
6224
6180
|
if (isMounted) {
|
|
6225
6181
|
const academeUser = {
|
|
6226
6182
|
keycloakUser: getKeycloakUser(),
|
|
6227
6183
|
};
|
|
6228
|
-
setCurrentUser({ ...academeUser, id:
|
|
6184
|
+
setCurrentUser({ ...academeUser, id: userProfileSub || "" });
|
|
6229
6185
|
}
|
|
6230
6186
|
return;
|
|
6231
6187
|
}
|
|
@@ -6243,7 +6199,7 @@ const SecurityProvider = ({ apiBaseUrl = "https://stg-api.academe.com.br", skipA
|
|
|
6243
6199
|
console.error("[SecurityProvider] Error fetching user data:", error);
|
|
6244
6200
|
}
|
|
6245
6201
|
}
|
|
6246
|
-
else if (!
|
|
6202
|
+
else if (!isAuthenticated && !isLoading) {
|
|
6247
6203
|
if (isMounted) {
|
|
6248
6204
|
setCurrentUser(null);
|
|
6249
6205
|
}
|
|
@@ -6254,17 +6210,16 @@ const SecurityProvider = ({ apiBaseUrl = "https://stg-api.academe.com.br", skipA
|
|
|
6254
6210
|
isMounted = false;
|
|
6255
6211
|
};
|
|
6256
6212
|
}, [
|
|
6257
|
-
|
|
6213
|
+
isAuthenticated,
|
|
6258
6214
|
isLoading,
|
|
6259
6215
|
getKeycloakUser,
|
|
6260
6216
|
services,
|
|
6261
6217
|
skipApiUserFetch,
|
|
6262
|
-
|
|
6263
|
-
isMobileAuth,
|
|
6218
|
+
userProfileSub,
|
|
6264
6219
|
]);
|
|
6265
6220
|
const refreshUserData = React2.useCallback(async () => {
|
|
6266
6221
|
setIsRefreshing(true);
|
|
6267
|
-
if (
|
|
6222
|
+
if (isAuthenticated) {
|
|
6268
6223
|
if (skipApiUserFetch) {
|
|
6269
6224
|
await auth.signinSilent();
|
|
6270
6225
|
const academeUser = {
|
|
@@ -6288,11 +6243,10 @@ const SecurityProvider = ({ apiBaseUrl = "https://stg-api.academe.com.br", skipA
|
|
|
6288
6243
|
}
|
|
6289
6244
|
}
|
|
6290
6245
|
setIsRefreshing(false);
|
|
6291
|
-
}, [
|
|
6246
|
+
}, [getKeycloakUser, services, skipApiUserFetch]);
|
|
6292
6247
|
// --- 5. Ações de Auth ---
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
console.log("[KC LOGOUT - OIDC]");
|
|
6248
|
+
const signOut = React2.useCallback(() => {
|
|
6249
|
+
console.log("[KC LOGOUT!]");
|
|
6296
6250
|
setCurrentUser(null);
|
|
6297
6251
|
auth.removeUser();
|
|
6298
6252
|
auth.signoutRedirect({
|
|
@@ -6300,32 +6254,6 @@ const SecurityProvider = ({ apiBaseUrl = "https://stg-api.academe.com.br", skipA
|
|
|
6300
6254
|
});
|
|
6301
6255
|
auth.clearStaleState();
|
|
6302
6256
|
}, [auth]);
|
|
6303
|
-
// SignOut para modo mobile
|
|
6304
|
-
const signOutMobile = React2.useCallback(() => {
|
|
6305
|
-
console.log("[KC LOGOUT - Mobile]");
|
|
6306
|
-
setCurrentUser(null);
|
|
6307
|
-
setMobileToken(undefined);
|
|
6308
|
-
setIsMobileAuth(false);
|
|
6309
|
-
// Limpar tokens globais
|
|
6310
|
-
if (typeof window !== "undefined") {
|
|
6311
|
-
window.accessToken = undefined;
|
|
6312
|
-
window.keycloakConfig = undefined;
|
|
6313
|
-
}
|
|
6314
|
-
currentTokenRef.current = undefined;
|
|
6315
|
-
// Notificar o app mobile para fazer logout
|
|
6316
|
-
if (window.ReactNativeWebView) {
|
|
6317
|
-
window.ReactNativeWebView.postMessage(JSON.stringify({ type: "LOGOUT_REQUESTED" }));
|
|
6318
|
-
}
|
|
6319
|
-
}, []);
|
|
6320
|
-
// SignOut unificado: escolhe automaticamente baseado no modo
|
|
6321
|
-
const signOut = React2.useCallback(() => {
|
|
6322
|
-
if (isMobileAuth) {
|
|
6323
|
-
signOutMobile();
|
|
6324
|
-
}
|
|
6325
|
-
else {
|
|
6326
|
-
signOutOidc();
|
|
6327
|
-
}
|
|
6328
|
-
}, [isMobileAuth, signOutMobile, signOutOidc]);
|
|
6329
6257
|
const hasSchool = React2.useCallback((schoolId) => {
|
|
6330
6258
|
if (hasRealmRole(exports.GLOBAL_ROLES.ADMIN_ACADEME)) {
|
|
6331
6259
|
return true;
|
|
@@ -6333,217 +6261,36 @@ const SecurityProvider = ({ apiBaseUrl = "https://stg-api.academe.com.br", skipA
|
|
|
6333
6261
|
return (currentUser?.institutionRegistrations?.some((registration) => registration.institutionId === schoolId) ?? false);
|
|
6334
6262
|
}, [hasRealmRole, currentUser?.institutionRegistrations]);
|
|
6335
6263
|
const goToLogin = React2.useCallback(() => {
|
|
6336
|
-
// Em modo mobile, notificar o app para fazer login
|
|
6337
|
-
if (isMobileAuth && window.ReactNativeWebView) {
|
|
6338
|
-
window.ReactNativeWebView.postMessage(JSON.stringify({ type: "LOGIN_REQUESTED" }));
|
|
6339
|
-
return Promise.resolve();
|
|
6340
|
-
}
|
|
6341
6264
|
return auth.signinRedirect();
|
|
6342
6265
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
6343
|
-
}, [
|
|
6266
|
+
}, []);
|
|
6344
6267
|
// Memoizar o value do context para evitar re-renders desnecessários
|
|
6345
6268
|
const contextValue = React2.useMemo(() => ({
|
|
6346
|
-
isInitialized:
|
|
6269
|
+
isInitialized: !isLoading || isRefreshing,
|
|
6347
6270
|
isTokenReady,
|
|
6348
|
-
isMobileAuth,
|
|
6349
6271
|
user: currentUser,
|
|
6350
6272
|
refreshUserData,
|
|
6351
6273
|
signOut,
|
|
6352
|
-
isAuthenticated: () =>
|
|
6274
|
+
isAuthenticated: () => isAuthenticated,
|
|
6353
6275
|
hasSchool,
|
|
6354
6276
|
goToLogin,
|
|
6355
6277
|
hasRealmRole,
|
|
6356
6278
|
hasClientRole,
|
|
6357
|
-
accessToken
|
|
6279
|
+
accessToken,
|
|
6358
6280
|
apiClient,
|
|
6359
6281
|
services,
|
|
6360
6282
|
}), [
|
|
6361
|
-
isMobileAuth,
|
|
6362
6283
|
isLoading,
|
|
6363
6284
|
isTokenReady,
|
|
6364
6285
|
currentUser,
|
|
6365
6286
|
refreshUserData,
|
|
6366
6287
|
signOut,
|
|
6367
|
-
|
|
6368
|
-
hasSchool,
|
|
6369
|
-
goToLogin,
|
|
6370
|
-
hasRealmRole,
|
|
6371
|
-
hasClientRole,
|
|
6372
|
-
effectiveToken,
|
|
6373
|
-
apiClient,
|
|
6374
|
-
services,
|
|
6375
|
-
]);
|
|
6376
|
-
return (jsxRuntime.jsx(SecurityContext.Provider, { value: contextValue, children: children }));
|
|
6377
|
-
};
|
|
6378
|
-
/**
|
|
6379
|
-
* SecurityProvider para modo mobile/iframe (sem OIDC)
|
|
6380
|
-
* Usado quando já temos um token injetado pelo parent/mobile
|
|
6381
|
-
* Evita montar o AuthProvider que tentaria fazer signinSilent
|
|
6382
|
-
*/
|
|
6383
|
-
const SecurityProviderMobileOnly = ({ apiBaseUrl = "https://stg-api.academe.com.br", skipApiUserFetch = false, children, }) => {
|
|
6384
|
-
const [currentUser, setCurrentUser] = React2.useState(null);
|
|
6385
|
-
const mobileToken = initialMobileState.token;
|
|
6386
|
-
const decodedToken = initialMobileState.decoded;
|
|
6387
|
-
// Ref para armazenar o token atual
|
|
6388
|
-
const currentTokenRef = React2.useRef(mobileToken);
|
|
6389
|
-
// Configurar token global na inicialização
|
|
6390
|
-
React2.useEffect(() => {
|
|
6391
|
-
if (mobileToken) {
|
|
6392
|
-
window.accessToken = mobileToken;
|
|
6393
|
-
currentTokenRef.current = mobileToken;
|
|
6394
|
-
}
|
|
6395
|
-
}, [mobileToken]);
|
|
6396
|
-
// API Client com token já configurado
|
|
6397
|
-
const apiClient = React2.useMemo(() => {
|
|
6398
|
-
const client = createAcademeApiClient(apiBaseUrl);
|
|
6399
|
-
client.use({
|
|
6400
|
-
async onRequest({ request }) {
|
|
6401
|
-
if (currentTokenRef.current) {
|
|
6402
|
-
request.headers.set("Authorization", `Bearer ${currentTokenRef.current}`);
|
|
6403
|
-
}
|
|
6404
|
-
return request;
|
|
6405
|
-
},
|
|
6406
|
-
});
|
|
6407
|
-
return client;
|
|
6408
|
-
}, [apiBaseUrl]);
|
|
6409
|
-
const services = React2.useMemo(() => {
|
|
6410
|
-
return createAcademeServices(apiClient);
|
|
6411
|
-
}, [apiClient]);
|
|
6412
|
-
const getKeycloakUser = React2.useCallback(() => {
|
|
6413
|
-
if (decodedToken) {
|
|
6414
|
-
return {
|
|
6415
|
-
email: decodedToken.email || "",
|
|
6416
|
-
name: decodedToken.given_name || "",
|
|
6417
|
-
lastName: decodedToken.family_name || "",
|
|
6418
|
-
avatar_url: decodedToken.avatar_url,
|
|
6419
|
-
};
|
|
6420
|
-
}
|
|
6421
|
-
return { email: "", name: "", lastName: "" };
|
|
6422
|
-
}, [decodedToken]);
|
|
6423
|
-
const hasRealmRole = React2.useCallback((role) => {
|
|
6424
|
-
return decodedToken?.realm_access?.roles?.includes(role) ?? false;
|
|
6425
|
-
}, [decodedToken]);
|
|
6426
|
-
const hasClientRole = React2.useCallback((role, clientId) => {
|
|
6427
|
-
if (!decodedToken?.resource_access)
|
|
6428
|
-
return false;
|
|
6429
|
-
if (clientId) {
|
|
6430
|
-
return (decodedToken.resource_access[clientId]?.roles?.includes(role) ?? false);
|
|
6431
|
-
}
|
|
6432
|
-
return Object.values(decodedToken.resource_access).some((resource) => resource.roles?.includes(role));
|
|
6433
|
-
}, [decodedToken]);
|
|
6434
|
-
// Fetch de dados do usuário
|
|
6435
|
-
React2.useEffect(() => {
|
|
6436
|
-
let isMounted = true;
|
|
6437
|
-
const fetchUserData = async () => {
|
|
6438
|
-
if (!mobileToken)
|
|
6439
|
-
return;
|
|
6440
|
-
if (skipApiUserFetch) {
|
|
6441
|
-
if (isMounted) {
|
|
6442
|
-
const academeUser = {
|
|
6443
|
-
keycloakUser: getKeycloakUser(),
|
|
6444
|
-
};
|
|
6445
|
-
setCurrentUser({ ...academeUser, id: decodedToken?.sub || "" });
|
|
6446
|
-
}
|
|
6447
|
-
return;
|
|
6448
|
-
}
|
|
6449
|
-
try {
|
|
6450
|
-
const response = await services.user.getMe();
|
|
6451
|
-
if (isMounted && response?.data?.data) {
|
|
6452
|
-
const academeUser = {
|
|
6453
|
-
...response.data.data,
|
|
6454
|
-
keycloakUser: getKeycloakUser(),
|
|
6455
|
-
};
|
|
6456
|
-
setCurrentUser(academeUser);
|
|
6457
|
-
}
|
|
6458
|
-
}
|
|
6459
|
-
catch (error) {
|
|
6460
|
-
console.error("[SecurityProviderMobileOnly] Error fetching user data:", error);
|
|
6461
|
-
}
|
|
6462
|
-
};
|
|
6463
|
-
fetchUserData();
|
|
6464
|
-
return () => {
|
|
6465
|
-
isMounted = false;
|
|
6466
|
-
};
|
|
6467
|
-
}, [mobileToken, getKeycloakUser, services, skipApiUserFetch, decodedToken?.sub]);
|
|
6468
|
-
const refreshUserData = React2.useCallback(async () => {
|
|
6469
|
-
if (!mobileToken)
|
|
6470
|
-
return;
|
|
6471
|
-
if (skipApiUserFetch) {
|
|
6472
|
-
const academeUser = {
|
|
6473
|
-
keycloakUser: getKeycloakUser(),
|
|
6474
|
-
};
|
|
6475
|
-
setCurrentUser(academeUser);
|
|
6476
|
-
return;
|
|
6477
|
-
}
|
|
6478
|
-
try {
|
|
6479
|
-
const response = await services.user.getMe();
|
|
6480
|
-
if (response?.data?.data) {
|
|
6481
|
-
const academeUser = {
|
|
6482
|
-
...response.data.data,
|
|
6483
|
-
keycloakUser: getKeycloakUser(),
|
|
6484
|
-
};
|
|
6485
|
-
setCurrentUser(academeUser);
|
|
6486
|
-
}
|
|
6487
|
-
}
|
|
6488
|
-
catch (error) {
|
|
6489
|
-
console.error("[SecurityProviderMobileOnly] Error refreshing user data:", error);
|
|
6490
|
-
}
|
|
6491
|
-
}, [mobileToken, getKeycloakUser, services, skipApiUserFetch]);
|
|
6492
|
-
const signOut = React2.useCallback(() => {
|
|
6493
|
-
console.log("[SecurityProviderMobileOnly] Logout");
|
|
6494
|
-
setCurrentUser(null);
|
|
6495
|
-
if (typeof window !== "undefined") {
|
|
6496
|
-
window.accessToken = undefined;
|
|
6497
|
-
window.keycloakConfig = undefined;
|
|
6498
|
-
}
|
|
6499
|
-
currentTokenRef.current = undefined;
|
|
6500
|
-
// Notificar o parent/app mobile para fazer logout
|
|
6501
|
-
if (window.ReactNativeWebView) {
|
|
6502
|
-
window.ReactNativeWebView.postMessage(JSON.stringify({ type: "LOGOUT_REQUESTED" }));
|
|
6503
|
-
}
|
|
6504
|
-
if (window.parent && window.parent !== window) {
|
|
6505
|
-
window.parent.postMessage({ type: "LOGOUT_REQUESTED" }, "*");
|
|
6506
|
-
}
|
|
6507
|
-
}, []);
|
|
6508
|
-
const hasSchool = React2.useCallback((schoolId) => {
|
|
6509
|
-
if (hasRealmRole(exports.GLOBAL_ROLES.ADMIN_ACADEME)) {
|
|
6510
|
-
return true;
|
|
6511
|
-
}
|
|
6512
|
-
return (currentUser?.institutionRegistrations?.some((registration) => registration.institutionId === schoolId) ?? false);
|
|
6513
|
-
}, [hasRealmRole, currentUser?.institutionRegistrations]);
|
|
6514
|
-
const goToLogin = React2.useCallback(() => {
|
|
6515
|
-
if (window.ReactNativeWebView) {
|
|
6516
|
-
window.ReactNativeWebView.postMessage(JSON.stringify({ type: "LOGIN_REQUESTED" }));
|
|
6517
|
-
}
|
|
6518
|
-
if (window.parent && window.parent !== window) {
|
|
6519
|
-
window.parent.postMessage({ type: "LOGIN_REQUESTED" }, "*");
|
|
6520
|
-
}
|
|
6521
|
-
return Promise.resolve();
|
|
6522
|
-
}, []);
|
|
6523
|
-
const contextValue = React2.useMemo(() => ({
|
|
6524
|
-
isInitialized: true,
|
|
6525
|
-
isTokenReady: true,
|
|
6526
|
-
isMobileAuth: true,
|
|
6527
|
-
user: currentUser,
|
|
6528
|
-
refreshUserData,
|
|
6529
|
-
signOut,
|
|
6530
|
-
isAuthenticated: () => !!mobileToken,
|
|
6531
|
-
hasSchool,
|
|
6532
|
-
goToLogin,
|
|
6533
|
-
hasRealmRole,
|
|
6534
|
-
hasClientRole,
|
|
6535
|
-
accessToken: mobileToken,
|
|
6536
|
-
apiClient,
|
|
6537
|
-
services,
|
|
6538
|
-
}), [
|
|
6539
|
-
currentUser,
|
|
6540
|
-
refreshUserData,
|
|
6541
|
-
signOut,
|
|
6542
|
-
mobileToken,
|
|
6288
|
+
isAuthenticated,
|
|
6543
6289
|
hasSchool,
|
|
6544
6290
|
goToLogin,
|
|
6545
6291
|
hasRealmRole,
|
|
6546
6292
|
hasClientRole,
|
|
6293
|
+
accessToken,
|
|
6547
6294
|
apiClient,
|
|
6548
6295
|
services,
|
|
6549
6296
|
]);
|