[upd] plankton
This commit is contained in:
parent
810965d255
commit
8611bb9445
2 changed files with 110 additions and 232 deletions
60
lib/plankton/plankton.d.ts
vendored
60
lib/plankton/plankton.d.ts
vendored
|
@ -4140,11 +4140,13 @@ declare namespace lib_plankton.auth.internal {
|
||||||
declare namespace lib_plankton.auth.oidc {
|
declare namespace lib_plankton.auth.oidc {
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
enum enum_state_label {
|
type type_token = string;
|
||||||
idle = "idle",
|
/**
|
||||||
wait = "wait",
|
*/
|
||||||
ready = "ready"
|
type type_userinfo = {
|
||||||
}
|
name: (null | string);
|
||||||
|
email: (null | string);
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
export type type_parameters_raw = {
|
export type type_parameters_raw = {
|
||||||
|
@ -4156,7 +4158,6 @@ declare namespace lib_plankton.auth.oidc {
|
||||||
url_redirect: string;
|
url_redirect: string;
|
||||||
scopes?: (null | Array<string>);
|
scopes?: (null | Array<string>);
|
||||||
label?: string;
|
label?: string;
|
||||||
login_url_mode?: (null | ("log" | "open"));
|
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
|
@ -4169,48 +4170,25 @@ declare namespace lib_plankton.auth.oidc {
|
||||||
url_redirect: string;
|
url_redirect: string;
|
||||||
scopes: Array<string>;
|
scopes: Array<string>;
|
||||||
label: string;
|
label: string;
|
||||||
login_url_mode: ("log" | "open");
|
|
||||||
};
|
};
|
||||||
/**
|
|
||||||
*/
|
|
||||||
type type_preparation = {
|
|
||||||
url: string;
|
|
||||||
label: string;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
type type_execute_input = void;
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
type type_control_input = ({
|
|
||||||
kind: "authorization_callback";
|
|
||||||
data: {
|
|
||||||
stuff: Record<string, string>;
|
|
||||||
cookie: string;
|
|
||||||
};
|
|
||||||
} | {
|
|
||||||
kind: "token_callback";
|
|
||||||
data: {
|
|
||||||
http_request: lib_plankton.http.type_request;
|
|
||||||
};
|
|
||||||
});
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
export type type_subject = {
|
export type type_subject = {
|
||||||
parameters: type_parameters;
|
parameters: type_parameters;
|
||||||
state: {
|
state: {};
|
||||||
id: string;
|
|
||||||
label: enum_state_label;
|
|
||||||
handlers: Array<{
|
|
||||||
resolve: ((result: string) => void);
|
|
||||||
reject: ((reason: Error) => void);
|
|
||||||
}>;
|
|
||||||
token: (null | string);
|
|
||||||
name: (null | string);
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
export function implementation_auth(parameters_raw: type_parameters_raw): type_auth<type_preparation, type_execute_input, type_control_input>;
|
export function make(parameters_raw: type_parameters_raw): type_subject;
|
||||||
|
/**
|
||||||
|
* @see https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
||||||
|
*/
|
||||||
|
export function authorization_url(subject: type_subject): string;
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function handle_authorization_callback(subject: type_subject, cookie: (null | string), stuff: Record<string, string>): Promise<{
|
||||||
|
token: type_token;
|
||||||
|
userinfo: type_userinfo;
|
||||||
|
}>;
|
||||||
export {};
|
export {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -14795,137 +14795,37 @@ var lib_plankton;
|
||||||
"label": (parameters_raw.label
|
"label": (parameters_raw.label
|
||||||
??
|
??
|
||||||
"OIDC"),
|
"OIDC"),
|
||||||
"login_url_mode": (parameters_raw.login_url_mode
|
|
||||||
??
|
|
||||||
"log"),
|
|
||||||
},
|
},
|
||||||
"state": {
|
"state": {}
|
||||||
"id": lib_plankton.random.generate_guid(),
|
|
||||||
"label": enum_state_label.idle,
|
|
||||||
"handlers": [],
|
|
||||||
"token": null,
|
|
||||||
"name": null,
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
oidc.make = make;
|
||||||
/**
|
/**
|
||||||
* @see https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
* @see https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
||||||
* @todo save state for different requests/users?
|
|
||||||
*/
|
*/
|
||||||
function login_prepare(subject) {
|
function authorization_url(subject) {
|
||||||
const url = lib_plankton.url.encode(Object.assign(lib_plankton.url.decode(subject.parameters.url_authorization), {
|
return lib_plankton.url.encode(Object.assign(lib_plankton.url.decode(subject.parameters.url_authorization), {
|
||||||
"query": lib_plankton.www_form.encode({
|
"query": lib_plankton.www_form.encode({
|
||||||
"response_type": "code",
|
"response_type": "code",
|
||||||
"client_id": subject.parameters.client_id,
|
"client_id": subject.parameters.client_id,
|
||||||
// "client_secret": subject.parameters.client_secret,
|
// "client_secret": subject.parameters.client_secret,
|
||||||
"scope": subject.parameters.scopes.join(" "),
|
"scope": subject.parameters.scopes.join(" "),
|
||||||
"state": subject.state.id,
|
"state": lib_plankton.random.generate_guid(),
|
||||||
"redirect_uri": subject.parameters.url_redirect,
|
"redirect_uri": subject.parameters.url_redirect,
|
||||||
})
|
})
|
||||||
}));
|
}));
|
||||||
/*
|
|
||||||
switch (subject.parameters.login_url_mode) {
|
|
||||||
case "log": {
|
|
||||||
lib_plankton.log.info(
|
|
||||||
"auth_login_url",
|
|
||||||
{
|
|
||||||
"url": url,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "open": {
|
|
||||||
const nm_opn = require("opn");
|
|
||||||
nm_opn(url);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
throw (new Error("invalid login_url_mode: " + subject.parameters.login_url_mode));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
return Promise.resolve({
|
|
||||||
"url": url,
|
|
||||||
"label": subject.parameters.label,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
oidc.authorization_url = authorization_url;
|
||||||
/**
|
/**
|
||||||
* @see https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
* https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest
|
||||||
*/
|
*/
|
||||||
function login_execute(subject, input) {
|
async function token_call(subject, cookie, code) {
|
||||||
switch (subject.state.label) {
|
const url = lib_plankton.url.decode(subject.parameters.url_token);
|
||||||
case enum_state_label.idle: {
|
const http_request = {
|
||||||
return (new Promise((resolve, reject) => {
|
|
||||||
subject.state = {
|
|
||||||
"id": subject.state.id,
|
|
||||||
"label": enum_state_label.wait,
|
|
||||||
"handlers": subject.state.handlers.concat([{ "resolve": resolve, "reject": reject }]),
|
|
||||||
"token": null,
|
|
||||||
"name": null,
|
|
||||||
};
|
|
||||||
}));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case enum_state_label.wait: {
|
|
||||||
return (new Promise((resolve, reject) => {
|
|
||||||
subject.state = {
|
|
||||||
"id": subject.state.id,
|
|
||||||
"label": enum_state_label.wait,
|
|
||||||
"handlers": subject.state.handlers.concat([{ "resolve": resolve, "reject": reject }]),
|
|
||||||
"token": null,
|
|
||||||
"name": null,
|
|
||||||
};
|
|
||||||
}));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case enum_state_label.ready: {
|
|
||||||
return Promise.resolve(subject.state.name);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @see https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
|
||||||
* @see https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest
|
|
||||||
* @see https://openid.net/specs/openid-connect-core-1_0.html#UserInfoRequest
|
|
||||||
* @todo authentication
|
|
||||||
*/
|
|
||||||
async function login_control(subject, input) {
|
|
||||||
switch (input.kind) {
|
|
||||||
default: {
|
|
||||||
throw (new Error("unhandled login control kind: " + input.kind));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "authorization_callback": {
|
|
||||||
lib_plankton.log.info("auth_authorization_callback_input", {
|
|
||||||
"input": input,
|
|
||||||
});
|
|
||||||
if ("error" in input.data.stuff) {
|
|
||||||
// query_parts["error"]
|
|
||||||
// query_parts["error_description"]
|
|
||||||
const handlers = subject.state.handlers;
|
|
||||||
subject.state = {
|
|
||||||
"id": subject.state.id,
|
|
||||||
"label": enum_state_label.idle,
|
|
||||||
"handlers": [],
|
|
||||||
"token": null,
|
|
||||||
"name": null,
|
|
||||||
};
|
|
||||||
handlers.forEach((handler) => {
|
|
||||||
handler.reject(new Error("failed"));
|
|
||||||
});
|
|
||||||
return Promise.resolve(undefined);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const code = input.data.stuff["code"];
|
|
||||||
const cookie = input.data.cookie;
|
|
||||||
const token_url = lib_plankton.url.decode(subject.parameters.url_token);
|
|
||||||
const token_http_request = {
|
|
||||||
"version": "HTTP/2",
|
"version": "HTTP/2",
|
||||||
"scheme": ((token_url.scheme === "http") ? "http" : "https"),
|
"scheme": ((url.scheme === "http") ? "http" : "https"),
|
||||||
"host": token_url.host,
|
"host": url.host,
|
||||||
"path": token_url.path,
|
"path": url.path,
|
||||||
"query": null,
|
"query": null,
|
||||||
"method": lib_plankton.http.enum_method.post,
|
"method": lib_plankton.http.enum_method.post,
|
||||||
"headers": {
|
"headers": {
|
||||||
|
@ -14945,22 +14845,27 @@ var lib_plankton;
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
lib_plankton.log.info("auth_token_request", {
|
lib_plankton.log.info("auth_token_request", {
|
||||||
"request": Object.assign(token_http_request, {
|
"request": Object.assign(http_request, {
|
||||||
"body": token_http_request.body.toString(),
|
"body": http_request.body.toString(),
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
const token_http_response = await lib_plankton.http.call(token_http_request);
|
const http_response = await lib_plankton.http.call(http_request);
|
||||||
lib_plankton.log.info("auth_token_response", {
|
lib_plankton.log.info("auth_token_response", {
|
||||||
"response": token_http_response,
|
"response": http_response,
|
||||||
});
|
});
|
||||||
const token_data = lib_plankton.json.decode(token_http_response.body.toString());
|
const data = lib_plankton.json.decode(http_response.body.toString());
|
||||||
const token = token_data["access_token"];
|
return Promise.resolve(data["access_token"]);
|
||||||
const userinfo_url = lib_plankton.url.decode(subject.parameters.url_userinfo);
|
}
|
||||||
const userinfo_http_request = {
|
/**
|
||||||
|
* @see https://openid.net/specs/openid-connect-core-1_0.html#UserInfoRequest
|
||||||
|
*/
|
||||||
|
async function userinfo_call(subject, cookie, token) {
|
||||||
|
const url = lib_plankton.url.decode(subject.parameters.url_userinfo);
|
||||||
|
const http_request = {
|
||||||
"version": "HTTP/2",
|
"version": "HTTP/2",
|
||||||
"scheme": ((userinfo_url.scheme === "http") ? "http" : "https"),
|
"scheme": ((url.scheme === "http") ? "http" : "https"),
|
||||||
"host": userinfo_url.host,
|
"host": url.host,
|
||||||
"path": userinfo_url.path,
|
"path": url.path,
|
||||||
"query": null,
|
"query": null,
|
||||||
"method": lib_plankton.http.enum_method.get,
|
"method": lib_plankton.http.enum_method.get,
|
||||||
"headers": {
|
"headers": {
|
||||||
|
@ -14970,43 +14875,38 @@ var lib_plankton;
|
||||||
"body": null
|
"body": null
|
||||||
};
|
};
|
||||||
lib_plankton.log.info("auth_userinfo_request", {
|
lib_plankton.log.info("auth_userinfo_request", {
|
||||||
"request": userinfo_http_request,
|
"request": http_request,
|
||||||
});
|
});
|
||||||
const userinfo_http_response = await lib_plankton.http.call(userinfo_http_request);
|
const http_response = await lib_plankton.http.call(http_request);
|
||||||
lib_plankton.log.info("auth_userinfo_response", {
|
lib_plankton.log.info("auth_userinfo_response", {
|
||||||
"response": userinfo_http_response,
|
"response": http_response,
|
||||||
});
|
});
|
||||||
const userinfo_data = lib_plankton.json.decode(userinfo_http_response.body.toString());
|
const data = lib_plankton.json.decode(http_response.body.toString());
|
||||||
const name = userinfo_data["name"]; // TODO: requires "profile" in "scope"
|
return Promise.resolve({
|
||||||
lib_plankton.log.info("auth_oidc_name", { "value": name });
|
"name": (data["name"] ?? null),
|
||||||
const handlers = subject.state.handlers;
|
"email": (data["email"] ?? null),
|
||||||
subject.state = {
|
|
||||||
"id": subject.state.id,
|
|
||||||
"label": enum_state_label.ready,
|
|
||||||
"handlers": [],
|
|
||||||
"token": token,
|
|
||||||
"name": name,
|
|
||||||
};
|
|
||||||
handlers.forEach((handler) => {
|
|
||||||
handler.resolve(subject.state.name);
|
|
||||||
});
|
});
|
||||||
return Promise.resolve(undefined);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
function implementation_auth(parameters_raw) {
|
async function handle_authorization_callback(subject, cookie, stuff) {
|
||||||
const subject = make(parameters_raw);
|
if (("error" in stuff)
|
||||||
return {
|
||
|
||||||
"login_prepare": () => login_prepare(subject),
|
(!("code" in stuff))
|
||||||
"login_execute": (input) => login_execute(subject, input),
|
||
|
||||||
"login_control": (input) => login_control(subject, input),
|
(cookie === null)) {
|
||||||
};
|
return Promise.reject(new Error("failed"));
|
||||||
}
|
}
|
||||||
oidc.implementation_auth = implementation_auth;
|
else {
|
||||||
|
const token = await token_call(subject, cookie, stuff["code"]);
|
||||||
|
const userinfo = await userinfo_call(subject, cookie, token);
|
||||||
|
return Promise.resolve({
|
||||||
|
"token": token,
|
||||||
|
"userinfo": userinfo,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
oidc.handle_authorization_callback = handle_authorization_callback;
|
||||||
})(oidc = auth.oidc || (auth.oidc = {}));
|
})(oidc = auth.oidc || (auth.oidc = {}));
|
||||||
})(auth = lib_plankton.auth || (lib_plankton.auth = {}));
|
})(auth = lib_plankton.auth || (lib_plankton.auth = {}));
|
||||||
})(lib_plankton || (lib_plankton = {}));
|
})(lib_plankton || (lib_plankton = {}));
|
||||||
|
|
Loading…
Add table
Reference in a new issue