native OIDC support
This commit is contained in:
@@ -5,7 +5,7 @@ import { adminApi } from "../Services/AdminApi";
|
||||
import { AuthTokenData, jwtTokenManager } from "../Services/JWTTokenManager";
|
||||
import { parse } from "query-string";
|
||||
import { openIDClient } from "../Services/OpenIDClient";
|
||||
import { FRONT_URL, DEBUG_IGNORE_SSL } from "../Enum/EnvironmentVariable"
|
||||
import { FRONT_URL, DEBUG_IGNORE_SSL, DISABLE_ANONYMOUS } from "../Enum/EnvironmentVariable"
|
||||
import Axios from "axios";
|
||||
import { AxiosRequestConfig } from "axios";
|
||||
import https from "https";
|
||||
@@ -69,7 +69,7 @@ export class AuthenticateController extends BaseController {
|
||||
await openIDClient.checkTokenAuth(authTokenData.hydraAccessToken);
|
||||
res.writeStatus("200");
|
||||
this.addCorsHeaders(res);
|
||||
return res.end(JSON.stringify({ authToken: token }));
|
||||
return res.end(JSON.stringify({ authToken: token, username: authTokenData.username }));
|
||||
} catch (err) {
|
||||
console.info("User was not connected", err);
|
||||
}
|
||||
@@ -81,10 +81,10 @@ export class AuthenticateController extends BaseController {
|
||||
if (!sub) {
|
||||
throw new Error("No sub in the response");
|
||||
}
|
||||
const authToken = jwtTokenManager.createAuthToken(sub, userInfo.access_token);
|
||||
const authToken = jwtTokenManager.createAuthToken(sub, userInfo.access_token, userInfo.username);
|
||||
res.writeStatus("200");
|
||||
this.addCorsHeaders(res);
|
||||
return res.end(JSON.stringify({ authToken }));
|
||||
return res.end(JSON.stringify({ authToken: authToken, username: userInfo.username }));
|
||||
} catch (e) {
|
||||
return this.errorToResponse(e, res);
|
||||
}
|
||||
@@ -173,32 +173,38 @@ export class AuthenticateController extends BaseController {
|
||||
res.onAborted(() => {
|
||||
console.warn("Login request was aborted");
|
||||
});
|
||||
let userUuid = v4();
|
||||
|
||||
const axiosConfig: AxiosRequestConfig = {};
|
||||
if (DISABLE_ANONYMOUS) {
|
||||
res.writeStatus("403 FORBIDDEN");
|
||||
res.end();
|
||||
} else {
|
||||
let userUuid = v4();
|
||||
|
||||
if (DEBUG_IGNORE_SSL) {
|
||||
const agent = new https.Agent({
|
||||
rejectUnauthorized: false,
|
||||
});
|
||||
axiosConfig.httpsAgent = agent;
|
||||
const axiosConfig: AxiosRequestConfig = {};
|
||||
|
||||
if (DEBUG_IGNORE_SSL) {
|
||||
const agent = new https.Agent({
|
||||
rejectUnauthorized: false,
|
||||
});
|
||||
axiosConfig.httpsAgent = agent;
|
||||
}
|
||||
|
||||
const response = await Axios.get(FRONT_URL, axiosConfig);
|
||||
|
||||
if (response.headers[ 'bstlyuserid' ]) {
|
||||
userUuid = response.headers[ 'bstlyuserid' ];
|
||||
}
|
||||
|
||||
const authToken = jwtTokenManager.createAuthToken(userUuid);
|
||||
res.writeStatus("200 OK");
|
||||
this.addCorsHeaders(res);
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
authToken,
|
||||
userUuid,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const response = await Axios.get(FRONT_URL, axiosConfig);
|
||||
|
||||
if (response.headers[ 'bstlyuserid' ]) {
|
||||
userUuid = response.headers[ 'bstlyuserid' ];
|
||||
}
|
||||
|
||||
const authToken = jwtTokenManager.createAuthToken(userUuid);
|
||||
res.writeStatus("200 OK");
|
||||
this.addCorsHeaders(res);
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
authToken,
|
||||
userUuid,
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -12,9 +12,10 @@ const PUSHER_HTTP_PORT = parseInt(process.env.PUSHER_HTTP_PORT || "8080") || 808
|
||||
export const SOCKET_IDLE_TIMER = parseInt(process.env.SOCKET_IDLE_TIMER as string) || 120; // maximum time (in second) without activity before a socket is closed. Should be greater than 60 seconds in order to cope for Chrome intensive throttling (https://developer.chrome.com/blog/timer-throttling-in-chrome-88/#intensive-throttling)
|
||||
|
||||
export const FRONT_URL = process.env.FRONT_URL || "http://localhost";
|
||||
export const OPID_CLIENT_ID = process.env.OPID_CLIENT_ID || "";
|
||||
export const OPID_CLIENT_SECRET = process.env.OPID_CLIENT_SECRET || "";
|
||||
export const OPID_CLIENT_ISSUER = process.env.OPID_CLIENT_ISSUER || "";
|
||||
export const DISABLE_ANONYMOUS = process.env.DISABLE_ANONYMOUS ? process.env.DISABLE_ANONYMOUS == "true" : false;
|
||||
export const OIDC_CLIENT_ID = process.env.OIDC_CLIENT_ID || "";
|
||||
export const OIDC_CLIENT_SECRET = process.env.OIDC_CLIENT_SECRET || "";
|
||||
export const OIDC_CLIENT_ISSUER = process.env.OIDC_CLIENT_ISSUER || "";
|
||||
export const DEBUG_IGNORE_SSL = process.env.DEBUG_IGNORE_SSL ? process.env.DEBUG_IGNORE_SSL == "true" : false;
|
||||
export const DEBUG_PUSHER_FORCE_ROOM_UPDATE = process.env.DEBUG_PUSHER_FORCE_ROOM_UPDATE ? process.env.DEBUG_PUSHER_FORCE_ROOM_UPDATE == "true" : false;
|
||||
|
||||
|
||||
@@ -5,14 +5,15 @@ import { TokenInterface } from "../Controller/AuthenticateController";
|
||||
import { adminApi, AdminBannedData } from "../Services/AdminApi";
|
||||
|
||||
export interface AuthTokenData {
|
||||
identifier: string; //will be a email if logged in or an uuid if anonymous
|
||||
identifier: string; //will be a sub (id) if logged in or an uuid if anonymous
|
||||
hydraAccessToken?: string;
|
||||
username?: string;
|
||||
}
|
||||
export const tokenInvalidException = "tokenInvalid";
|
||||
|
||||
class JWTTokenManager {
|
||||
public createAuthToken(identifier: string, hydraAccessToken?: string) {
|
||||
return Jwt.sign({ identifier, hydraAccessToken }, SECRET_KEY, { expiresIn: "30d" });
|
||||
public createAuthToken(identifier: string, hydraAccessToken?: string, username?: string) {
|
||||
return Jwt.sign({ identifier, hydraAccessToken, username }, SECRET_KEY, { expiresIn: "30d" });
|
||||
}
|
||||
|
||||
public verifyJWTToken(token: string, ignoreExpiration: boolean = false): AuthTokenData {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Issuer, Client, IntrospectionResponse } from "openid-client";
|
||||
import { OPID_CLIENT_ID, OPID_CLIENT_SECRET, OPID_CLIENT_ISSUER, FRONT_URL } from "../Enum/EnvironmentVariable";
|
||||
import { OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, OIDC_CLIENT_ISSUER, FRONT_URL } from "../Enum/EnvironmentVariable";
|
||||
|
||||
const opidRedirectUri = FRONT_URL + "/jwt";
|
||||
|
||||
@@ -8,10 +8,10 @@ class OpenIDClient {
|
||||
|
||||
private initClient(): Promise<Client> {
|
||||
if (!this.issuerPromise) {
|
||||
this.issuerPromise = Issuer.discover(OPID_CLIENT_ISSUER).then((issuer) => {
|
||||
this.issuerPromise = Issuer.discover(OIDC_CLIENT_ISSUER).then((issuer) => {
|
||||
return new issuer.Client({
|
||||
client_id: OPID_CLIENT_ID,
|
||||
client_secret: OPID_CLIENT_SECRET,
|
||||
client_id: OIDC_CLIENT_ID,
|
||||
client_secret: OIDC_CLIENT_SECRET,
|
||||
redirect_uris: [opidRedirectUri],
|
||||
response_types: ["code"],
|
||||
});
|
||||
@@ -32,7 +32,7 @@ class OpenIDClient {
|
||||
});
|
||||
}
|
||||
|
||||
public getUserInfo(code: string, nonce: string): Promise<{ email: string; sub: string; access_token: string }> {
|
||||
public getUserInfo(code: string, nonce: string): Promise<{ email: string; sub: string; access_token: string; username: string }> {
|
||||
return this.initClient().then((client) => {
|
||||
return client.callback(opidRedirectUri, { code }, { nonce }).then((tokenSet) => {
|
||||
return client.userinfo(tokenSet).then((res) => {
|
||||
@@ -41,6 +41,7 @@ class OpenIDClient {
|
||||
email: res.email as string,
|
||||
sub: res.sub,
|
||||
access_token: tokenSet.access_token as string,
|
||||
username: (res.preferred_username || res.username || res.nickname || res.name || res.email) as string,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user