116 lines
3.2 KiB
TypeScript
116 lines
3.2 KiB
TypeScript
import express from "express";
|
|
import * as oidc from "oidc-provider";
|
|
import * as dotenv from "dotenv";
|
|
import { join, dirname } from "path";
|
|
import { fileURLToPath } from "url";
|
|
import Handlebars from "handlebars";
|
|
import assert from "assert";
|
|
import { readFileSync } from "fs";
|
|
|
|
dotenv.config({ quiet: true });
|
|
const AUTH_PROVIDER_PORT = Number(process.env.AUTH_PROVIDER_PORT) || 3000;
|
|
const ACCOUNT_USERNAME = String(process.env.ACCOUNT_USERNAME) || "unknown_user";
|
|
const PROVIDER_ISSUER = String(process.env.PROVIDER_ISSUER);
|
|
const provider_login_page = Handlebars.compile(
|
|
readFileSync(join(dirname(fileURLToPath(import.meta.url)), "provider.html"), {
|
|
encoding: "utf-8",
|
|
}),
|
|
);
|
|
|
|
const provider = new oidc.Provider(PROVIDER_ISSUER, {
|
|
features: {
|
|
registration: {
|
|
enabled: true,
|
|
initialAccessToken: false,
|
|
issueRegistrationAccessToken: true,
|
|
},
|
|
devInteractions: {
|
|
enabled: false,
|
|
},
|
|
},
|
|
async findAccount(ctx, id) {
|
|
assert(id == ACCOUNT_USERNAME);
|
|
return {
|
|
accountId: id,
|
|
async claims(use, scope) {
|
|
return { sub: id };
|
|
},
|
|
};
|
|
},
|
|
});
|
|
provider.proxy = true;
|
|
|
|
|
|
|
|
const app = express();
|
|
app.enable("trust proxy");
|
|
|
|
app.get("/interaction/:uid", async (req, res) => {
|
|
const details = await provider.interactionDetails(req, res);
|
|
const login = details.prompt.name == "login";
|
|
res.header("Content-Type", "text/html");
|
|
res.send(provider_login_page({ login, details: JSON.stringify(details, null, 3) }));
|
|
});
|
|
|
|
app.post("/interaction/:uid", async (req, res) => {
|
|
const interactionDetails = await provider.interactionDetails(req, res);
|
|
const details = interactionDetails.prompt.details;
|
|
let grant: oidc.Grant | undefined;
|
|
if (interactionDetails.grantId) {
|
|
grant = await provider.Grant.find(interactionDetails.grantId);
|
|
}
|
|
if (typeof grant == "undefined") {
|
|
if (
|
|
interactionDetails.session &&
|
|
typeof interactionDetails.params.client_id == "string"
|
|
) {
|
|
grant = new provider.Grant({
|
|
accountId: interactionDetails.session.accountId,
|
|
clientId: interactionDetails.params.client_id,
|
|
});
|
|
}
|
|
}
|
|
let consent: { consent: { grantId: string } } | {} = {};
|
|
if (typeof grant != "undefined") {
|
|
if (details.missingOIDCScope instanceof Array) {
|
|
grant.addOIDCScope(details.missingOIDCScope.join(" "));
|
|
}
|
|
if (details.missingOIDCClaims instanceof Array) {
|
|
grant.addOIDCClaims(details.missingOIDCClaims);
|
|
}
|
|
if (details.missingResourceScopes) {
|
|
for (const [indicator, scopes] of Object.entries(
|
|
details.missingResourceScopes,
|
|
)) {
|
|
grant.addResourceScope(indicator, scopes.join(" "));
|
|
}
|
|
}
|
|
|
|
const grantId = await grant.save();
|
|
if (interactionDetails.grantId != grantId) {
|
|
consent = {
|
|
consent: {
|
|
grantId,
|
|
},
|
|
};
|
|
}
|
|
}
|
|
const result = {
|
|
login: {
|
|
accountId: ACCOUNT_USERNAME,
|
|
},
|
|
...consent,
|
|
};
|
|
const error = {
|
|
error: "access_denied",
|
|
};
|
|
return provider.interactionFinished(req, res, result); // result object below
|
|
});
|
|
|
|
app.use(provider.callback());
|
|
|
|
app.listen(AUTH_PROVIDER_PORT, (err) => {
|
|
if (!err) {
|
|
console.log(`Auth provider listening on port ${AUTH_PROVIDER_PORT}`);
|
|
}
|
|
});
|