BARON-SSO 로그인 API 요청경로 수정
This commit is contained in:
117
server.js
117
server.js
@@ -16,7 +16,10 @@ const {
|
||||
REDIRECT_URI,
|
||||
JWKS_URI,
|
||||
SESSION_SECRET,
|
||||
ERROR_LOCALE_PATH
|
||||
ERROR_LOCALE_PATH,
|
||||
PHONE_HEADLESS_LINK_INIT_ENDPOINT,
|
||||
PHONE_HEADLESS_LINK_POLL_ENDPOINT,
|
||||
PHONE_HEADLESS_LOGIN_ENDPOINT
|
||||
} = process.env;
|
||||
|
||||
const SESSION_SECRET_VALUE = SESSION_SECRET || 'itam-headless-session-secret';
|
||||
@@ -207,6 +210,8 @@ const ensureSsoConfig = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const buildIssuerEndpoint = (overrideUrl, fallbackPath) => new URL(overrideUrl || fallbackPath, ISSUER).toString();
|
||||
|
||||
const base64Url = (input) => Buffer.from(input).toString('base64url');
|
||||
|
||||
const sha256Base64Url = (input) => crypto.createHash('sha256').update(input).digest('base64url');
|
||||
@@ -561,9 +566,36 @@ const runHeadlessSsoLogin = async ({ loginId, password }) => {
|
||||
};
|
||||
};
|
||||
|
||||
const resolveAuthenticatedPhoneLogin = async ({ redirectTo, cookies, discovery, authState }) => {
|
||||
const resolution = await resolveRedirects(redirectTo, cookies);
|
||||
if (resolution.isErrorRedirect) {
|
||||
return { status: 'error_redirect', redirectTo: resolution.finalUrl };
|
||||
}
|
||||
|
||||
if (!resolution.code) {
|
||||
throw new Error('Authorization code not found after phone redirect resolution');
|
||||
}
|
||||
|
||||
const tokenResponse = await exchangeAuthorizationCode(
|
||||
resolution.code,
|
||||
discovery,
|
||||
authState.codeVerifier
|
||||
);
|
||||
const idTokenPayload = decodeJwtPayload(tokenResponse.id_token);
|
||||
|
||||
return {
|
||||
status: 'authenticated',
|
||||
tokens: tokenResponse,
|
||||
profile: idTokenPayload
|
||||
};
|
||||
};
|
||||
|
||||
const initHeadlessPhoneLogin = async ({ loginId }) => {
|
||||
const { discovery, cookies, loginChallenge, authState } = await beginAuthorizationFlow();
|
||||
const headlessEndpoint = new URL('/api/v1/auth/headless/link/init', ISSUER).toString();
|
||||
const headlessEndpoint = buildIssuerEndpoint(
|
||||
PHONE_HEADLESS_LOGIN_ENDPOINT || PHONE_HEADLESS_LINK_INIT_ENDPOINT,
|
||||
'/api/v1/auth/headless/phone-login'
|
||||
);
|
||||
|
||||
const initRes = await fetch(headlessEndpoint, {
|
||||
method: 'POST',
|
||||
@@ -581,11 +613,25 @@ const initHeadlessPhoneLogin = async ({ loginId }) => {
|
||||
|
||||
const nextCookies = appendCookies(cookies, initRes);
|
||||
const initBody = await parseJsonSafely(initRes);
|
||||
if (!initRes.ok || !initBody?.pendingRef) {
|
||||
if (!initRes.ok) {
|
||||
throw new Error(`Phone link init failed: ${initRes.status} ${JSON.stringify(initBody)}`);
|
||||
}
|
||||
|
||||
if (initBody?.redirectTo) {
|
||||
return resolveAuthenticatedPhoneLogin({
|
||||
redirectTo: initBody.redirectTo,
|
||||
cookies: nextCookies,
|
||||
discovery,
|
||||
authState
|
||||
});
|
||||
}
|
||||
|
||||
if (!initBody?.pendingRef) {
|
||||
throw new Error(`Phone link init failed: ${initRes.status} ${JSON.stringify(initBody)}`);
|
||||
}
|
||||
|
||||
return {
|
||||
status: 'pending',
|
||||
discovery,
|
||||
cookies: nextCookies,
|
||||
pendingRef: initBody.pendingRef,
|
||||
@@ -597,7 +643,10 @@ const initHeadlessPhoneLogin = async ({ loginId }) => {
|
||||
};
|
||||
|
||||
const pollHeadlessPhoneLogin = async (pendingContext) => {
|
||||
const pollEndpoint = new URL('/api/v1/auth/headless/link/poll', ISSUER).toString();
|
||||
const pollEndpoint = buildIssuerEndpoint(
|
||||
PHONE_HEADLESS_LINK_POLL_ENDPOINT,
|
||||
'/api/v1/auth/headless/link/poll'
|
||||
);
|
||||
const pollRes = await fetch(pollEndpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@@ -615,27 +664,12 @@ const pollHeadlessPhoneLogin = async (pendingContext) => {
|
||||
const pollBody = await parseJsonSafely(pollRes);
|
||||
|
||||
if (pollRes.ok && pollBody?.redirectTo) {
|
||||
const resolution = await resolveRedirects(pollBody.redirectTo, nextCookies);
|
||||
if (resolution.isErrorRedirect) {
|
||||
return { status: 'error_redirect', redirectTo: resolution.finalUrl };
|
||||
}
|
||||
|
||||
if (!resolution.code) {
|
||||
throw new Error('Authorization code not found after phone redirect resolution');
|
||||
}
|
||||
|
||||
const tokenResponse = await exchangeAuthorizationCode(
|
||||
resolution.code,
|
||||
pendingContext.discovery,
|
||||
pendingContext.authState.codeVerifier
|
||||
);
|
||||
const idTokenPayload = decodeJwtPayload(tokenResponse.id_token);
|
||||
|
||||
return {
|
||||
status: 'authenticated',
|
||||
tokens: tokenResponse,
|
||||
profile: idTokenPayload
|
||||
};
|
||||
return resolveAuthenticatedPhoneLogin({
|
||||
redirectTo: pollBody.redirectTo,
|
||||
cookies: nextCookies,
|
||||
discovery: pendingContext.discovery,
|
||||
authState: pendingContext.authState
|
||||
});
|
||||
}
|
||||
|
||||
const statusCode = pollBody?.code || pollBody?.error;
|
||||
@@ -745,17 +779,40 @@ app.post('/api/auth/headless/phone/init', async (req, res) => {
|
||||
}
|
||||
|
||||
try {
|
||||
const pendingLogin = await initHeadlessPhoneLogin({ loginId });
|
||||
const phoneLoginResult = await initHeadlessPhoneLogin({ loginId });
|
||||
|
||||
if (phoneLoginResult.status === 'error_redirect') {
|
||||
return res.status(403).json({ redirectTo: phoneLoginResult.redirectTo, code: 'tenant_not_allowed' });
|
||||
}
|
||||
|
||||
if (phoneLoginResult.status === 'authenticated') {
|
||||
req.session.user = {
|
||||
loginId,
|
||||
profile: phoneLoginResult.profile,
|
||||
tokens: {
|
||||
accessToken: phoneLoginResult.tokens.access_token,
|
||||
idToken: phoneLoginResult.tokens.id_token,
|
||||
expiresIn: phoneLoginResult.tokens.expires_in,
|
||||
scope: phoneLoginResult.tokens.scope,
|
||||
tokenType: phoneLoginResult.tokens.token_type
|
||||
}
|
||||
};
|
||||
registerSessionIdentity(req.sessionID, req.session.user);
|
||||
await saveSession(req);
|
||||
return res.json({ success: true, status: 'authenticated', user: req.session.user });
|
||||
}
|
||||
|
||||
req.session.pendingPhoneLogin = {
|
||||
...pendingLogin,
|
||||
...phoneLoginResult,
|
||||
startedAt: Date.now()
|
||||
};
|
||||
await saveSession(req);
|
||||
res.json({
|
||||
success: true,
|
||||
pendingRef: pendingLogin.pendingRef,
|
||||
intervalMs: pendingLogin.intervalMs,
|
||||
expiresInMs: pendingLogin.expiresInMs,
|
||||
status: 'pending',
|
||||
pendingRef: phoneLoginResult.pendingRef,
|
||||
intervalMs: phoneLoginResult.intervalMs,
|
||||
expiresInMs: phoneLoginResult.expiresInMs,
|
||||
message: '인증 링크를 발송했습니다. 모바일에서 승인 후 잠시만 기다려주세요.'
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user