Files
code-center/src/routes/user/web-login.ts

132 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { app } from '@/app.ts';
import { User } from '@/models/user.ts';
import MD5 from 'crypto-js/md5.js';
import { authCan } from '@/auth/index.ts';
import jsonwebtoken from 'jsonwebtoken';
import { redis } from '@/app.ts';
import { createCookie, clearCookie } from './me.ts';
import z from 'zod';
app
.route({
path: 'user',
key: 'webLogin',
description: 'web登录接口配合插件使用',
middleware: [authCan],
metadata: {
args: {
loginToken: z.string().describe('web登录令牌服务端生成客户端保持一致'),
sign: z.string().describe('签名,服务端生成,客户端保持一致'),
randomId: z.string().describe('随机字符串,服务端和客户端保持一致'),
}
}
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const token = ctx.query.token;
const { loginToken, sign, randomId } = ctx.query || {};
const setErrorLoginTokenRedis = async (loginToken: string) => {
await redis.set(loginToken, JSON.stringify({}), 'EX', 2 * 60); // 2分钟
};
if (!tokenUser) {
if (token) {
console.log('web-login, token', ' run clearCookie', token, tokenUser);
// clearCookie(ctx);
} else {
// const message = 'token is expired, please login in web page. ';
}
try {
ctx.res.setHeader('Content-Type', 'text/html');
const createRedirectHtml = () => {
const reqUrl = ctx.req.url;
return `
<html lang="zh-CN">
<body>
<h1>login with web page</h1>
<a href="${reqUrl}">${reqUrl}</a>
<script>
const redirect = new URL('${reqUrl}', window.location.origin);
const encodeRedirect = encodeURIComponent(redirect.toString());
const toPage = new URL('/root/center/login/?user-check=true&redirect='+encodeRedirect, window.location.origin);
setTimeout(() => {
window.location.href = toPage.toString();
}, 1000);
</script>
</body>
</html>
`;
};
ctx.res.end(createRedirectHtml());
} catch (e) {
await setErrorLoginTokenRedis(loginToken);
ctx.throw(400, 'token is expired and redirect error');
}
return;
}
if (!loginToken) {
await setErrorLoginTokenRedis(loginToken);
ctx.throw(400, 'loginToken is required');
}
if (!randomId) {
await setErrorLoginTokenRedis(loginToken);
ctx.throw(400, 'randomId is required');
}
const tokenSecret = 'xiao' + randomId;
if (sign) {
let payload: any = {};
try {
payload = jsonwebtoken.verify(loginToken, tokenSecret);
} catch (e) {
await setErrorLoginTokenRedis(loginToken);
ctx.throw(400, 'loginToken error');
}
const { timestamp } = payload;
const checkSign = MD5(`${tokenSecret}${timestamp}`).toString();
if (sign !== checkSign) {
await setErrorLoginTokenRedis(loginToken);
ctx.throw(400, 'sign error');
}
}
const user = await User.findByPk(tokenUser.id);
if (!user) {
await setErrorLoginTokenRedis(loginToken);
ctx.throw(400, 'user not found');
}
const data = await user.createToken(null, 'jwks', { loginWith: 'cli' });
await redis.set(loginToken, JSON.stringify(data), 'EX', 10 * 60); // 10分钟
ctx.body = 'success';
})
.addTo(app);
app
.route({
path: 'user',
key: 'checkLoginStatus',
description: '循环检查登陆状态',
})
.define(async (ctx) => {
const { loginToken } = ctx.query;
if (!loginToken) {
ctx.throw(400, 'loginToken is required');
}
// const data = tokenData[loginToken];
const data = await redis.get(loginToken);
if (data) {
const token = JSON.parse(data);
if (token.accessToken) {
ctx.body = token;
createCookie(token, ctx);
} else {
ctx.throw(500, 'Checked error Failed, login failed, please login again');
}
await redis.expire(loginToken, 2 * 60); // 2分钟
} else {
ctx.throw(400, 'Checked Failed');
}
})
.addTo(app);