Initialize
This commit is contained in:
146
index.js
Executable file
146
index.js
Executable file
@@ -0,0 +1,146 @@
|
||||
const express = require("express");
|
||||
const session = require("express-session");
|
||||
const { Issuer } = require("openid-client");
|
||||
const { createTemplateRenderer } = require("./utils/templates.js");
|
||||
const { Client } = require("pg");
|
||||
const { generateOtp } = require("mobilepass");
|
||||
|
||||
require("dotenv").config();
|
||||
|
||||
const PORT = process.env.PORT || 3000;
|
||||
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
app.use(
|
||||
session({
|
||||
secret: process.env.COOKIE_SECRET,
|
||||
saveUninitialized: false,
|
||||
cookie: { maxAge: 10 * 60 * 1000 },
|
||||
})
|
||||
);
|
||||
|
||||
const renderHtml = createTemplateRenderer({
|
||||
useCache: process.env.NODE_ENV === "production",
|
||||
});
|
||||
|
||||
(async () => {
|
||||
const issuer = await Issuer.discover(process.env.OIDC_WELL_KNOWN_URL);
|
||||
const client = new issuer.Client({
|
||||
client_id: process.env.CLIENT_ID,
|
||||
client_secret: process.env.CLIENT_SECRET,
|
||||
response_types: ["code"],
|
||||
});
|
||||
|
||||
const pg = new Client();
|
||||
await pg.connect();
|
||||
|
||||
await pg.query(`
|
||||
CREATE TABLE IF NOT EXISTS counters (
|
||||
id SERIAL PRIMARY KEY,
|
||||
counter INTEGER NOT NULL
|
||||
)
|
||||
`);
|
||||
|
||||
app.get("/", async (req, res) => {
|
||||
if (req.session.user) {
|
||||
const counter = await pg.query(
|
||||
"SELECT counter FROM counters WHERE id = 1"
|
||||
).then((result) => {
|
||||
if (result.rows.length > 0) {
|
||||
return result.rows[0].counter;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
res.send(
|
||||
renderHtml("index.html", {
|
||||
USERNAME: req.session.user.sub,
|
||||
OTP: await generateOtp(process.env.ACTIVATION_CODE, counter),
|
||||
COUNTER: counter,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
res.send(renderHtml("login.html"));
|
||||
}
|
||||
});
|
||||
|
||||
const stateMap = {};
|
||||
|
||||
app.post("/authorize", (req, res) => {
|
||||
const { redirectUri } = req.body;
|
||||
if (!redirectUri) {
|
||||
return res.send(
|
||||
renderHtml("login-fail.html", {
|
||||
ERROR: "redirectUri missing or invalid",
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const state = Math.random().toString(36).substring(2);
|
||||
stateMap[state] = { redirectUri };
|
||||
|
||||
const url = client.authorizationUrl({
|
||||
scope: "openid profile",
|
||||
redirect_uri: redirectUri,
|
||||
state,
|
||||
});
|
||||
|
||||
// 回傳 JSON 給前端,讓前端負責導向
|
||||
res.json({ redirect: url });
|
||||
});
|
||||
|
||||
app.get("/callback", async (req, res) => {
|
||||
try {
|
||||
const state = req.query.state;
|
||||
const { redirectUri } = stateMap[state];
|
||||
|
||||
const params = client.callbackParams(req);
|
||||
const tokenSet = await client.callback(redirectUri, params, { state });
|
||||
const userinfo = await client.userinfo(tokenSet.access_token);
|
||||
|
||||
req.session.user = userinfo;
|
||||
res.redirect("/");
|
||||
} catch (error) {
|
||||
res.send(renderHtml("login-fail.html", { ERROR: error }));
|
||||
}
|
||||
});
|
||||
|
||||
app.put("/counter", (req, res) => {
|
||||
if (req.session.user) {
|
||||
const { counter } = req.body;
|
||||
try {
|
||||
pg.query(
|
||||
`
|
||||
INSERT INTO counters (id, counter)
|
||||
VALUES (1, $1)
|
||||
ON CONFLICT (id) DO UPDATE SET counter = $1
|
||||
`,
|
||||
[counter]
|
||||
);
|
||||
res.json({ success: true });
|
||||
}
|
||||
catch (error) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
} else {
|
||||
res.status(401).json({ success: false, error: "Unauthorized" });
|
||||
}
|
||||
});
|
||||
|
||||
app.post("/logout", (req, res) => {
|
||||
req.session.destroy(() => {
|
||||
res.redirect("/");
|
||||
});
|
||||
});
|
||||
|
||||
process.on("SIGINT", async () => {
|
||||
await pg.end();
|
||||
console.log("Database connection closed.");
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`listening at http://localhost:${PORT}`);
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user