[mod] auth:oidc
This commit is contained in:
parent
62af4bf485
commit
6521f60601
4 changed files with 122 additions and 95 deletions
10
lib/plankton/plankton.d.ts
vendored
10
lib/plankton/plankton.d.ts
vendored
|
@ -1,11 +1,11 @@
|
||||||
/**
|
/**
|
||||||
* @author fenris
|
* @author fenris
|
||||||
*/
|
*/
|
||||||
type int = number;
|
declare type int = number;
|
||||||
/**
|
/**
|
||||||
* @author fenris
|
* @author fenris
|
||||||
*/
|
*/
|
||||||
type float = number;
|
declare type float = number;
|
||||||
declare var process: any;
|
declare var process: any;
|
||||||
declare var require: any;
|
declare var require: any;
|
||||||
declare class Buffer {
|
declare class Buffer {
|
||||||
|
@ -22,7 +22,7 @@ declare namespace lib_plankton.base {
|
||||||
/**
|
/**
|
||||||
* @author fenris
|
* @author fenris
|
||||||
*/
|
*/
|
||||||
type type_pseudopointer<type_value> = {
|
declare type type_pseudopointer<type_value> = {
|
||||||
value: type_value;
|
value: type_value;
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
|
@ -2186,7 +2186,7 @@ declare namespace lib_plankton.storage.memory {
|
||||||
clear(): Promise<void>;
|
clear(): Promise<void>;
|
||||||
write(key: any, value: any): Promise<boolean>;
|
write(key: any, value: any): Promise<boolean>;
|
||||||
delete(key: any): Promise<void>;
|
delete(key: any): Promise<void>;
|
||||||
read(key: any): Promise<Awaited<type_item>>;
|
read(key: any): Promise<type_item>;
|
||||||
search(term: any): Promise<{
|
search(term: any): Promise<{
|
||||||
key: string;
|
key: string;
|
||||||
preview: string;
|
preview: string;
|
||||||
|
@ -4175,7 +4175,7 @@ declare namespace lib_plankton.auth.oidc {
|
||||||
*/
|
*/
|
||||||
export type type_subject = {
|
export type type_subject = {
|
||||||
parameters: type_parameters;
|
parameters: type_parameters;
|
||||||
state: {};
|
cases: Record<string, {}>;
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1568,7 +1568,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||||
function step(op) {
|
function step(op) {
|
||||||
if (f) throw new TypeError("Generator is already executing.");
|
if (f) throw new TypeError("Generator is already executing.");
|
||||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
while (_) try {
|
||||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||||
switch (op[0]) {
|
switch (op[0]) {
|
||||||
|
@ -6564,7 +6564,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||||
function step(op) {
|
function step(op) {
|
||||||
if (f) throw new TypeError("Generator is already executing.");
|
if (f) throw new TypeError("Generator is already executing.");
|
||||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
while (_) try {
|
||||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||||
switch (op[0]) {
|
switch (op[0]) {
|
||||||
|
@ -9886,7 +9886,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||||
function step(op) {
|
function step(op) {
|
||||||
if (f) throw new TypeError("Generator is already executing.");
|
if (f) throw new TypeError("Generator is already executing.");
|
||||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
while (_) try {
|
||||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||||
switch (op[0]) {
|
switch (op[0]) {
|
||||||
|
@ -13940,7 +13940,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||||
function step(op) {
|
function step(op) {
|
||||||
if (f) throw new TypeError("Generator is already executing.");
|
if (f) throw new TypeError("Generator is already executing.");
|
||||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
while (_) try {
|
||||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||||
switch (op[0]) {
|
switch (op[0]) {
|
||||||
|
@ -14771,12 +14771,12 @@ var lib_plankton;
|
||||||
(function (oidc) {
|
(function (oidc) {
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
let enum_state_label;
|
let enum_condition;
|
||||||
(function (enum_state_label) {
|
(function (enum_condition) {
|
||||||
enum_state_label["idle"] = "idle";
|
enum_condition["idle"] = "idle";
|
||||||
enum_state_label["wait"] = "wait";
|
enum_condition["wait"] = "wait";
|
||||||
enum_state_label["ready"] = "ready";
|
enum_condition["ready"] = "ready";
|
||||||
})(enum_state_label || (enum_state_label = {}));
|
})(enum_condition || (enum_condition = {}));
|
||||||
;
|
;
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
|
@ -14796,26 +14796,38 @@ var lib_plankton;
|
||||||
??
|
??
|
||||||
"OIDC"),
|
"OIDC"),
|
||||||
},
|
},
|
||||||
"state": {}
|
"cases": {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
oidc.make = make;
|
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
|
||||||
*/
|
*/
|
||||||
function authorization_url(subject) {
|
function authorization_url(subject, state) {
|
||||||
return lib_plankton.url.encode(Object.assign(lib_plankton.url.decode(subject.parameters.url_authorization), {
|
const url = 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": lib_plankton.random.generate_guid(),
|
"state": state,
|
||||||
"redirect_uri": subject.parameters.url_redirect,
|
"redirect_uri": subject.parameters.url_redirect,
|
||||||
})
|
})
|
||||||
}));
|
}));
|
||||||
|
return url;
|
||||||
}
|
}
|
||||||
oidc.authorization_url = authorization_url;
|
oidc.authorization_url = authorization_url;
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
function prepare_login(subject) {
|
||||||
|
const state = lib_plankton.random.generate_guid();
|
||||||
|
subject.cases[state] = {};
|
||||||
|
return {
|
||||||
|
"state": state,
|
||||||
|
"authorization_url": authorization_url(state),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
oidc.prepare_login = prepare_login;
|
||||||
/**
|
/**
|
||||||
* https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest
|
* https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest
|
||||||
*/
|
*/
|
||||||
|
@ -14890,20 +14902,26 @@ var lib_plankton;
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
async function handle_authorization_callback(subject, cookie, stuff) {
|
async function handle_authorization_callback(subject, cookie, stuff) {
|
||||||
if (("error" in stuff)
|
const state = stuff["state"];
|
||||||
||
|
if (!(state in subject.cases)) {
|
||||||
(!("code" in stuff))
|
return Promise.reject(new Error("no case for this state"));
|
||||||
||
|
|
||||||
(cookie === null)) {
|
|
||||||
return Promise.reject(new Error("failed"));
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const token = await token_call(subject, cookie, stuff["code"]);
|
if (("error" in stuff)
|
||||||
const userinfo = await userinfo_call(subject, cookie, token);
|
||
|
||||||
return Promise.resolve({
|
(!("code" in stuff))
|
||||||
"token": token,
|
||
|
||||||
"userinfo": userinfo,
|
(cookie === null)) {
|
||||||
});
|
return Promise.reject(new Error("failed"));
|
||||||
|
}
|
||||||
|
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.handle_authorization_callback = handle_authorization_callback;
|
||||||
|
|
|
@ -56,8 +56,8 @@ namespace _zeitbild.api
|
||||||
name : (null | string);
|
name : (null | string);
|
||||||
email : (null | string);
|
email : (null | string);
|
||||||
};
|
};
|
||||||
} = await lib_plankton.auth.oidc.handle_authorization_callback(
|
redirect_uri_template : string;
|
||||||
_zeitbild.auth.oidc_subject(),
|
} = await _zeitbild.auth.oidc_handle_authorization_callback(
|
||||||
(stuff.headers["Cookie"] ?? stuff.headers["cookie"] ?? null),
|
(stuff.headers["Cookie"] ?? stuff.headers["cookie"] ?? null),
|
||||||
stuff.query_parameters
|
stuff.query_parameters
|
||||||
);
|
);
|
||||||
|
@ -94,7 +94,6 @@ namespace _zeitbild.api
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const redirect_uri_template : string = _zeitbild.auth.oidc_get_redirect_uri_template("foo");
|
|
||||||
return Promise.resolve(
|
return Promise.resolve(
|
||||||
{
|
{
|
||||||
"status_code": 200,
|
"status_code": 200,
|
||||||
|
@ -102,7 +101,7 @@ namespace _zeitbild.api
|
||||||
"<html><head><meta http-equiv=\"refresh\" content=\"0; url={{url}}\" /></head><body></body></html>",
|
"<html><head><meta http-equiv=\"refresh\" content=\"0; url={{url}}\" /></head><body></body></html>",
|
||||||
{
|
{
|
||||||
"url": lib_plankton.string.coin(
|
"url": lib_plankton.string.coin(
|
||||||
redirect_uri_template,
|
data.redirect_uri_template,
|
||||||
{
|
{
|
||||||
"session_key": session_key,
|
"session_key": session_key,
|
||||||
}
|
}
|
||||||
|
|
130
source/auth.ts
130
source/auth.ts
|
@ -11,6 +11,13 @@ namespace _zeitbild.auth
|
||||||
) = null;
|
) = null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
let _subject_oidc : (null | lib_plankton.auth.oidc.type_subject) = null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
let _oidc_redict_uri_template_map : (
|
let _oidc_redict_uri_template_map : (
|
||||||
|
@ -20,30 +27,6 @@ namespace _zeitbild.auth
|
||||||
) = null;
|
) = null;
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
export function oidc_subject(
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return lib_plankton.auth.oidc.make(
|
|
||||||
{
|
|
||||||
"url_authorization": _zeitbild.conf.get().authentication.data.url_authorization,
|
|
||||||
"url_token": _zeitbild.conf.get().authentication.data.url_token,
|
|
||||||
"url_userinfo": _zeitbild.conf.get().authentication.data.url_userinfo,
|
|
||||||
"client_id": _zeitbild.conf.get().authentication.data.client_id,
|
|
||||||
"client_secret": _zeitbild.conf.get().authentication.data.client_secret,
|
|
||||||
"url_redirect": (_zeitbild.conf.get().authentication.data.backend_url_base + "/session/oidc"),
|
|
||||||
"scopes": [
|
|
||||||
"openid",
|
|
||||||
"profile",
|
|
||||||
"email",
|
|
||||||
],
|
|
||||||
"label": _zeitbild.conf.get().authentication.data.label,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
export function oidc_get_redirect_uri_template(
|
export function oidc_get_redirect_uri_template(
|
||||||
|
@ -54,12 +37,6 @@ namespace _zeitbild.auth
|
||||||
throw (new Error("apparently not initialized yet"));
|
throw (new Error("apparently not initialized yet"));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
lib_plankton.log.info(
|
|
||||||
"oidc_redirect_uri_templates",
|
|
||||||
{
|
|
||||||
"val": lib_plankton.map.dump(_oidc_redict_uri_template_map),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return _oidc_redict_uri_template_map.get(key);
|
return _oidc_redict_uri_template_map.get(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,6 +65,22 @@ lib_plankton.log.info(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "oidc": {
|
case "oidc": {
|
||||||
|
_subject_oidc = lib_plankton.auth.oidc.make(
|
||||||
|
{
|
||||||
|
"url_authorization": _zeitbild.conf.get().authentication.data.url_authorization,
|
||||||
|
"url_token": _zeitbild.conf.get().authentication.data.url_token,
|
||||||
|
"url_userinfo": _zeitbild.conf.get().authentication.data.url_userinfo,
|
||||||
|
"client_id": _zeitbild.conf.get().authentication.data.client_id,
|
||||||
|
"client_secret": _zeitbild.conf.get().authentication.data.client_secret,
|
||||||
|
"url_redirect": (_zeitbild.conf.get().authentication.data.backend_url_base + "/session/oidc"),
|
||||||
|
"scopes": [
|
||||||
|
"openid",
|
||||||
|
"profile",
|
||||||
|
"email",
|
||||||
|
],
|
||||||
|
"label": _zeitbild.conf.get().authentication.data.label,
|
||||||
|
}
|
||||||
|
);
|
||||||
_oidc_redict_uri_template_map = lib_plankton.map.simplemap.implementation_map(
|
_oidc_redict_uri_template_map = lib_plankton.map.simplemap.implementation_map(
|
||||||
lib_plankton.map.simplemap.make(
|
lib_plankton.map.simplemap.make(
|
||||||
)
|
)
|
||||||
|
@ -113,21 +106,21 @@ lib_plankton.log.info(
|
||||||
{
|
{
|
||||||
switch (_zeitbild.conf.get().authentication.kind) {
|
switch (_zeitbild.conf.get().authentication.kind) {
|
||||||
case "oidc": {
|
case "oidc": {
|
||||||
const subject : lib_plankton.auth.oidc.type_subject = oidc_subject();
|
if (_subject_oidc === null) {
|
||||||
if (_oidc_redict_uri_template_map === null) {
|
throw (new Error("not initialized yet"));
|
||||||
throw (new Error("apparently not initialized yet"));
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
const stuff : {state : string; authorization_url : string;} = lib_plankton.auth.oidc.prepare_login(_subject_oidc);
|
||||||
_oidc_redict_uri_template_map.set(
|
_oidc_redict_uri_template_map.set(
|
||||||
"foo", // TODO proper key
|
stuff.state,
|
||||||
input["oidc_redirect_uri_template"]
|
input["oidc_redirect_uri_template"]
|
||||||
);
|
);
|
||||||
return Promise.resolve(
|
return Promise.resolve(
|
||||||
{
|
{
|
||||||
"kind": "oidc",
|
"kind": "oidc",
|
||||||
"data": {
|
"data": {
|
||||||
"url": lib_plankton.auth.oidc.authorization_url(subject),
|
"url": stuff.authorization_url,
|
||||||
"label": subject.parameters.label,
|
"label": _zeitbild.conf.get().authentication.data.label,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -157,31 +150,48 @@ lib_plankton.log.info(
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
export function execute(
|
export function oidc_handle_authorization_callback(
|
||||||
input : any
|
cookie : (null | string),
|
||||||
) : Promise<string>
|
data : Record<string, string>
|
||||||
|
) : Promise<
|
||||||
|
{
|
||||||
|
token : string;
|
||||||
|
userinfo : {
|
||||||
|
name : (null | string);
|
||||||
|
email : (null | string);
|
||||||
|
};
|
||||||
|
redirect_uri_template : string;
|
||||||
|
}
|
||||||
|
>
|
||||||
{
|
{
|
||||||
if (_subject === null) {
|
const state : string = data["state"];
|
||||||
return Promise.reject(new Error("not initialized yet"));
|
const result : {
|
||||||
}
|
token : string;
|
||||||
else {
|
userinfo : {
|
||||||
return _subject.login_execute(input);
|
name : (null | string);
|
||||||
}
|
email : (null | string);
|
||||||
}
|
};
|
||||||
|
} = await lib_plankton.auth.oidc.handle_authorization_callback(
|
||||||
|
_oidc_subject,
|
||||||
/**
|
cookie,
|
||||||
*/
|
data
|
||||||
export function control(
|
);
|
||||||
input : any
|
return Promise.resolve<
|
||||||
) : Promise<void>
|
{
|
||||||
{
|
token : string;
|
||||||
if (_subject === null) {
|
userinfo : {
|
||||||
return Promise.reject(new Error("not initialized yet"));
|
name : (null | string);
|
||||||
}
|
email : (null | string);
|
||||||
else {
|
};
|
||||||
return _subject.login_control(input);
|
redirect_uri_template : string;
|
||||||
}
|
}
|
||||||
|
>(
|
||||||
|
{
|
||||||
|
"token": result.token,
|
||||||
|
"userinfo": result.userinfo,
|
||||||
|
"redirect_uri_template": _oidc_redict_uri_template_map.get(state),
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue