更新依赖,优化认证流程;添加聊天框动画效果,优化消息处理逻辑
This commit is contained in:
@@ -1,14 +1,58 @@
|
||||
import { NextAuthOptions } from "next-auth";
|
||||
import { JWT } from "next-auth/jwt";
|
||||
import KeycloakProvider from "next-auth/providers/keycloak";
|
||||
import Avatar from "@assets/avatar/avatar-small.jpeg";
|
||||
|
||||
type KeycloakTokenResponse = {
|
||||
access_token: string;
|
||||
expires_in: number;
|
||||
refresh_token?: string;
|
||||
};
|
||||
|
||||
const keycloakIssuer = process.env.KEYCLOAK_ISSUER!;
|
||||
const keycloakClientId = process.env.KEYCLOAK_CLIENT_ID!;
|
||||
const keycloakClientSecret = process.env.KEYCLOAK_CLIENT_SECRET!;
|
||||
const keycloakTokenEndpoint = `${keycloakIssuer.replace(/\/$/, "")}/protocol/openid-connect/token`;
|
||||
|
||||
const refreshAccessToken = async (token: JWT): Promise<JWT> => {
|
||||
if (!token.refreshToken) {
|
||||
return { ...token, error: "RefreshAccessTokenError" };
|
||||
}
|
||||
|
||||
const body = new URLSearchParams({
|
||||
grant_type: "refresh_token",
|
||||
client_id: keycloakClientId,
|
||||
client_secret: keycloakClientSecret,
|
||||
refresh_token: token.refreshToken,
|
||||
});
|
||||
|
||||
const response = await fetch(keycloakTokenEndpoint, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||
body,
|
||||
});
|
||||
const refreshed = (await response.json()) as KeycloakTokenResponse;
|
||||
|
||||
if (!response.ok || !refreshed.access_token || typeof refreshed.expires_in !== "number") {
|
||||
return { ...token, error: "RefreshAccessTokenError" };
|
||||
}
|
||||
|
||||
return {
|
||||
...token,
|
||||
accessToken: refreshed.access_token,
|
||||
accessTokenExpires: Date.now() + refreshed.expires_in * 1000,
|
||||
refreshToken: refreshed.refresh_token ?? token.refreshToken,
|
||||
error: undefined,
|
||||
};
|
||||
};
|
||||
|
||||
const authOptions: NextAuthOptions = {
|
||||
// Configure one or more authentication providers
|
||||
providers: [
|
||||
KeycloakProvider({
|
||||
clientId: process.env.KEYCLOAK_CLIENT_ID!,
|
||||
clientSecret: process.env.KEYCLOAK_CLIENT_SECRET!,
|
||||
issuer: process.env.KEYCLOAK_ISSUER!,
|
||||
clientId: keycloakClientId,
|
||||
clientSecret: keycloakClientSecret,
|
||||
issuer: keycloakIssuer,
|
||||
profile(profile) {
|
||||
return {
|
||||
id: profile.sub,
|
||||
@@ -25,10 +69,26 @@ const authOptions: NextAuthOptions = {
|
||||
if (profile?.sub) {
|
||||
token.sub = profile.sub;
|
||||
}
|
||||
if (account?.access_token) {
|
||||
token.accessToken = account.access_token;
|
||||
|
||||
if (account) {
|
||||
if (account.access_token) {
|
||||
token.accessToken = account.access_token;
|
||||
}
|
||||
if (account.refresh_token) {
|
||||
token.refreshToken = account.refresh_token;
|
||||
}
|
||||
if (typeof account.expires_at === "number") {
|
||||
token.accessTokenExpires = account.expires_at * 1000;
|
||||
}
|
||||
token.error = undefined;
|
||||
return token;
|
||||
}
|
||||
return token;
|
||||
|
||||
if (typeof token.accessTokenExpires === "number" && Date.now() < token.accessTokenExpires - 30_000) {
|
||||
return token;
|
||||
}
|
||||
|
||||
return refreshAccessToken(token);
|
||||
},
|
||||
session: async ({ session, token }) => {
|
||||
if (session.user && token.sub) {
|
||||
@@ -37,6 +97,9 @@ const authOptions: NextAuthOptions = {
|
||||
if (token.accessToken) {
|
||||
session.accessToken = token.accessToken;
|
||||
}
|
||||
if (token.error) {
|
||||
session.error = token.error;
|
||||
}
|
||||
return session;
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user