update
This commit is contained in:
@@ -3,6 +3,8 @@ import type { z } from 'zod'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
import { Loader2 } from 'lucide-vue-next'
|
||||
import { useForm } from 'vee-validate'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useKeycloak } from "~/composables/useKeycloack"
|
||||
|
||||
interface Props {
|
||||
schema: z.ZodSchema<any>
|
||||
@@ -14,7 +16,6 @@ const props = defineProps<Props>()
|
||||
const emit = defineEmits<{
|
||||
submit: [data: any]
|
||||
sso: []
|
||||
response: [state: string]
|
||||
}>()
|
||||
|
||||
const { handleSubmit, defineField, errors, meta } = useForm({
|
||||
@@ -36,24 +37,24 @@ const onSubmit = handleSubmit(async (values) => {
|
||||
}
|
||||
})
|
||||
|
||||
const { initKeycloak, getProfile, login } = useKeycloak()
|
||||
const profile = ref<any>(null)
|
||||
|
||||
onMounted(async () => {
|
||||
await initKeycloak('check-sso')
|
||||
profile.value = getProfile()
|
||||
console.log(profile)
|
||||
})
|
||||
|
||||
const onSSO = (async () => {
|
||||
console.log("Emitting SSO...")
|
||||
try {
|
||||
await emit('sso')
|
||||
const redirect = window.location.origin + '/auth/sso'
|
||||
await login({ redirectUri: redirect })
|
||||
} catch (error) {
|
||||
console.error('Call SSO failed:', error)
|
||||
}
|
||||
});
|
||||
|
||||
const test = useRoute()
|
||||
const responseSSO = test.hash
|
||||
|
||||
if (responseSSO != null && responseSSO != '') {
|
||||
console.log("Getting Response SSO...")
|
||||
await emit('response', responseSSO)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -93,11 +94,7 @@ if (responseSSO != null && responseSSO != '') {
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
<a href="/auth/keycloak/login">Login with Keycloak</a>
|
||||
|
||||
<Button @click="onSSO" target="_blank">
|
||||
Login SSO
|
||||
</Button>
|
||||
|
||||
<span>success {{ responseSSO }}</span>
|
||||
</template>
|
||||
|
||||
@@ -41,121 +41,10 @@ async function onSubmit(data: LoginFormData) {
|
||||
|
||||
isLoading.value = false
|
||||
}
|
||||
|
||||
const config = useRuntimeConfig()
|
||||
// const store = useKeycloak()
|
||||
|
||||
const state = reactive({
|
||||
loggedIn: false
|
||||
})
|
||||
|
||||
async function onSSO() {
|
||||
console.log("=================== on SSO! ===================")
|
||||
const config = useRuntimeConfig()
|
||||
|
||||
const keycloak = new Keycloak({
|
||||
url: config.public.KEYCLOAK_URL,
|
||||
realm: config.public.KEYCLOAK_REALM,
|
||||
clientId: config.public.NUXT_OIDC_PROVIDERS_KEYCLOAK_CLIENT_ID,
|
||||
});
|
||||
|
||||
try {
|
||||
const authenticatedResult = await keycloak.init({ onLoad: 'login-required' }); // Or 'login-required'
|
||||
// seelah line ini aku mek paham logic e tapi faktane dunno
|
||||
const nuxtApp = useNuxtApp()
|
||||
nuxtApp.provide('keycloak', keycloak);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Keycloak initialization failed:', error);
|
||||
}
|
||||
|
||||
// const initOptions = {
|
||||
// url: config.public.KEYCLOAK_URL,
|
||||
// realm: config.public.KEYCLOAK_REALM,
|
||||
// clientId: config.public.KEYCLOAK_ID,
|
||||
// onLoad: 'login-required'
|
||||
// }
|
||||
|
||||
// const keycloak = new Keycloak(initOptions)
|
||||
// console.log("=================== balik gak se! ===================")
|
||||
// keycloak
|
||||
// .init({ onLoad: initOptions.onLoad })
|
||||
// .then((auth) => {
|
||||
// console.log(auth)
|
||||
// if (!auth) {
|
||||
// window.location.reload()
|
||||
// } else {
|
||||
// // store.setup(keycloak)
|
||||
// state.loggedIn = true
|
||||
// }
|
||||
// })
|
||||
|
||||
console.log("=================== onto login fes! ===================")
|
||||
// if (state.loggedIn) {
|
||||
// const result = await xfetch('/api/v1/authentication/login-fes', 'POST', {
|
||||
// data: keycloak,
|
||||
// })
|
||||
|
||||
// if (result.success) {
|
||||
// const { data: rawdata, meta } = result.body
|
||||
// if (meta.status === 'verified') {
|
||||
// login(rawdata)
|
||||
// navigateTo('/')
|
||||
// }
|
||||
// } else {
|
||||
// if (result.errors) {
|
||||
// Object.entries(result.errors).forEach(
|
||||
// ([field, errorInfo]: [string, any]) => (apiErrors.value[field] = errorInfo.message),
|
||||
// )
|
||||
// } else {
|
||||
// apiErrors.value.general = result.error?.message || result.message || 'Login failed'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// const urlSSO =
|
||||
// config.public.KEYCLOAK_ISSUER +
|
||||
// '/protocol/openid-connect/auth?client_id=' +
|
||||
// config.public.KEYCLOAK_ID +
|
||||
// '&scope=openid%20email%20profile&response_type=code&redirect_uri=' +
|
||||
// config.public.KEYCLOAK_LOGOUT_REDIRECT +
|
||||
// '%2Fapi%2Fauth%2Fcallback%2Fkeycloak&state=AKf-WHWdL822V3LaNS5MSFzJ96-VHp6FUXlXxIAzXXM&code_challenge=nXOcGLLlA1NtXI4RM4hL59iP_I_yQAsUDd5sAOkKBF4&code_challenge_method=S256'
|
||||
// await navigateTo(urlSSO,
|
||||
// {
|
||||
// open: { target: '_blank' },
|
||||
// external: true
|
||||
// })
|
||||
}
|
||||
|
||||
async function onResponseSSO(authenticatedResult: string) {
|
||||
console.log("=================== onto login fes!!! ===================")
|
||||
console.log(authenticatedResult)
|
||||
if (authenticatedResult) {
|
||||
const result = await xfetch('/api/v1/authentication/login-fes', 'POST', {
|
||||
data: authenticatedResult,
|
||||
})
|
||||
|
||||
if (result.success) {
|
||||
const { data: rawdata, meta } = result.body
|
||||
if (meta.status === 'verified') {
|
||||
login(rawdata)
|
||||
navigateTo('/')
|
||||
}
|
||||
} else {
|
||||
if (result.errors) {
|
||||
Object.entries(result.errors).forEach(
|
||||
([field, errorInfo]: [string, any]) => (apiErrors.value[field] = errorInfo.message),
|
||||
)
|
||||
} else {
|
||||
apiErrors.value.general = result.error?.message || result.message || 'Login failed'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AppAuthLogin :schema="loginSchema" :is-loading="isLoading" @submit="onSubmit" @sso="onSSO" @response="onResponseSSO" />
|
||||
<AppAuthLogin :schema="loginSchema" :is-loading="isLoading" @submit="onSubmit" />
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
import Keycloak from "keycloak-js";
|
||||
import { ref, computed, onBeforeMount } from "vue";
|
||||
|
||||
let kc: any | null = null;
|
||||
|
||||
const initialized = ref(false);
|
||||
const authenticated = ref(false);
|
||||
const token = ref<string | null>(null);
|
||||
const profile = ref<any>(null);
|
||||
|
||||
export function useKeycloak() {
|
||||
const initKeycloak = async (onLoad: "login-required" | "check-sso" = "check-sso") => {
|
||||
if (kc) return kc;
|
||||
kc = new Keycloak({
|
||||
url: config.public.KEYCLOAK_URL,
|
||||
realm: config.public.KEYCLOAK_REALM,
|
||||
clientId: config.public.CLIENT_ID,
|
||||
});
|
||||
|
||||
try {
|
||||
const initOptions = {
|
||||
onLoad,
|
||||
promiseType: "native" as const,
|
||||
pkceMethod: "S256" as const,
|
||||
};
|
||||
console.log(kc.url)
|
||||
authenticated.value = await kc.init(initOptions);
|
||||
initialized.value = true;
|
||||
token.value = kc.token ?? null;
|
||||
if (authenticated.value) {
|
||||
try {
|
||||
profile.value = await kc.loadUserProfile();
|
||||
} catch (e) {
|
||||
profile.value = null;
|
||||
}
|
||||
}
|
||||
// automatically update token in background
|
||||
kc.onTokenExpired = async () => {
|
||||
try {
|
||||
const refreshed = await kc.updateToken(30);
|
||||
token.value = kc?.token ?? null;
|
||||
if (!refreshed) {
|
||||
// token not refreshed but still valid
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn("Failed to refresh token", err);
|
||||
}
|
||||
};
|
||||
return kc;
|
||||
} catch (err) {
|
||||
console.log(authenticated)
|
||||
console.error("Keycloak init xyz failed", err);
|
||||
initialized.value = true;
|
||||
authenticated.value = false;
|
||||
return kc;
|
||||
}
|
||||
};
|
||||
|
||||
const login = (options?: Keycloak.KeycloakLoginOptions) => {
|
||||
if (!kc) throw new Error("Keycloak not initialized");
|
||||
return kc.login(options);
|
||||
};
|
||||
|
||||
const logout = (redirectUri?: string) => {
|
||||
if (!kc) throw new Error("Keycloak not initialized");
|
||||
return kc.logout({ redirectUri });
|
||||
};
|
||||
|
||||
const getToken = () => token.value;
|
||||
const isAuthenticated = computed(() => authenticated.value);
|
||||
const getProfile = () => profile.value;
|
||||
|
||||
// init on client automatically
|
||||
// onBeforeMount(() => {
|
||||
// // try check-sso silently
|
||||
// if (!initialized.value) initKeycloak("check-sso");
|
||||
// });
|
||||
|
||||
const apiErrors = ref<Record<string, string>>({})
|
||||
|
||||
const getResponse = async () => {
|
||||
console.log("=================== onto login fes!!! ===================")
|
||||
const params = {
|
||||
token: token.value,
|
||||
user: profile.value
|
||||
}
|
||||
const result = await xfetch('/api/v1/authentication/login-fes', 'POST', {
|
||||
data: params,
|
||||
})
|
||||
|
||||
if (result.success) {
|
||||
const { data: rawdata, meta } = result.body
|
||||
if (meta.status === 'verified') {
|
||||
login(rawdata)
|
||||
navigateTo('/')
|
||||
}
|
||||
} else {
|
||||
if (result.errors) {
|
||||
Object.entries(result.errors).forEach(
|
||||
([field, errorInfo]: [string, any]) => (apiErrors.value[field] = errorInfo.message),
|
||||
)
|
||||
} else {
|
||||
apiErrors.value.general = result.error?.message || result.message || 'Login failed'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
initKeycloak,
|
||||
login,
|
||||
logout,
|
||||
getToken,
|
||||
isAuthenticated,
|
||||
getProfile,
|
||||
getResponse,
|
||||
};
|
||||
}
|
||||
@@ -1,14 +1,18 @@
|
||||
export default defineNuxtRouteMiddleware((to) => {
|
||||
import { useKeycloak } from "~/composables/useKeycloack"
|
||||
|
||||
export default defineNuxtRouteMiddleware(async (to) => {
|
||||
if (to.meta.public) return
|
||||
|
||||
const { $pinia } = useNuxtApp()
|
||||
const oidc = useOidcAuth();
|
||||
|
||||
const { initKeycloak, isAuthenticated} = useKeycloak(); // global composable
|
||||
await initKeycloak("check-sso");
|
||||
|
||||
if (import.meta.client) {
|
||||
const userStore = useUserStore($pinia)
|
||||
if (!userStore.isAuthenticated && !oidc.loggedIn) {
|
||||
if (!userStore.isAuthenticated && !isAuthenticated.value) {
|
||||
// await login({ redirectUri: window.location.origin + to.fullPath });
|
||||
return navigateTo('/auth/login')
|
||||
// oidc.login('dev');
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
+24
-7
@@ -1,13 +1,30 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
layout: 'blank',
|
||||
public: true,
|
||||
})
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useKeycloak } from "~/composables/useKeycloack"
|
||||
|
||||
const test = useRoute()
|
||||
const temp = test.hash
|
||||
const error = ref<string | null>(null)
|
||||
const { initKeycloak, isAuthenticated, getResponse } = useKeycloak()
|
||||
const profile = ref<any>(null)
|
||||
const token = ref<string | null>(null);
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
// Initialize Keycloak with login-required to ensure tokens set (Keycloak will process the code/state returned)
|
||||
await initKeycloak('login-required')
|
||||
// small delay to allow token propagation
|
||||
if (isAuthenticated.value) {
|
||||
await getResponse()
|
||||
}
|
||||
} catch (err: any) {
|
||||
error.value = err?.message || String(err)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span>success {{ temp }}</span>
|
||||
<div style="max-width:720px;margin:40px auto">
|
||||
<h2>Processing login...</h2>
|
||||
<p v-if="error">Terjadi error: {{ error }}</p>
|
||||
<p v-else>Mohon tunggu, sedang memproses otentikasi dan mengarahkan Anda ...</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user