diff --git a/Backend/package.json b/Backend/package.json index 66562ea..4774d78 100644 --- a/Backend/package.json +++ b/Backend/package.json @@ -1,6 +1,5 @@ { "name": "@hibas123/openauth-backend", - "version": "1.1.2", "main": "lib/index.js", "author": "Fabian Stamm ", "license": "MIT", diff --git a/Backend/src/api/middlewares/client.ts b/Backend/src/api/middlewares/client.ts index c6f44a7..59353e5 100644 --- a/Backend/src/api/middlewares/client.ts +++ b/Backend/src/api/middlewares/client.ts @@ -5,6 +5,7 @@ import { validateJWT } from "../../keys"; import User from "../../models/user"; import Mail from "../../models/mail"; import { OAuthJWT } from "../../helper/jwt"; +import Logging from "@hibas123/nodelogging"; export function GetClientAuthMiddleware( checksecret = true, @@ -67,13 +68,16 @@ export function GetClientApiAuthMiddleware(permissions?: string[]) { return async (req: Request, res: Response, next: NextFunction) => { try { const invalid_err = new RequestError( - req.__("You are not logged in or your login is expired"), + req.__("Unauthorized"), HttpStatusCode.UNAUTHORIZED ); let token = (req.query.access_token as string) || (req.headers.authorization as string); - if (!token) throw invalid_err; + if (!token) { + Logging.debug("No token found. Searched in query (access_token) and header (authorization)"); + throw invalid_err; + } if (token.toLowerCase().startsWith("bearer ")) token = token.substring(7); @@ -82,22 +86,31 @@ export function GetClientApiAuthMiddleware(permissions?: string[]) { try { data = await validateJWT(token); } catch (err) { + Logging.debug("Invalid JWT", err.message); throw invalid_err; } let user = await User.findOne({ uid: data.user }); - if (!user) throw invalid_err; + if (!user) { + Logging.debug("User not found"); + throw invalid_err; + } let client = await Client.findOne({ client_id: data.application }); - if (!client) throw invalid_err; + if (!client) { + Logging.debug("Client not found"); + throw invalid_err; + } if ( permissions && (!data.permissions || !permissions.every((e) => data.permissions.indexOf(e) >= 0)) - ) + ) { + Logging.debug("Invalid permissions"); throw invalid_err; + } req.user = user; req.client = client; diff --git a/Backend/src/api/oauth/index.ts b/Backend/src/api/oauth/index.ts index a68bad6..562f88a 100644 --- a/Backend/src/api/oauth/index.ts +++ b/Backend/src/api/oauth/index.ts @@ -3,8 +3,9 @@ import GetAuthRoute from "./auth"; import JWTRoute from "./jwt"; import Public from "./public"; import RefreshTokenRoute from "./refresh"; +import ProfileRoute from "./profile"; -const OAuthRoue: Router = Router(); +const OAuthRoute: Router = Router(); /** * @api {post} /oauth/auth * @apiName OAuthAuth @@ -19,7 +20,7 @@ const OAuthRoue: Router = Router(); * @apiParam {String} state State, that will be passed to redirect_uri for client * @apiParam {String} nored Deactivates the Redirect response from server and instead returns the redirect URI in JSON response */ -OAuthRoue.post("/auth", GetAuthRoute(false)); +OAuthRoute.post("/auth", GetAuthRoute(false)); /** * @api {get} /oauth/jwt @@ -32,7 +33,7 @@ OAuthRoue.post("/auth", GetAuthRoute(false)); * * @apiSuccess {String} token The JWT that allowes the application to access the recources granted for refresh token */ -OAuthRoue.get("/jwt", JWTRoute); +OAuthRoute.get("/jwt", JWTRoute); /** * @api {get} /oauth/public @@ -43,7 +44,7 @@ OAuthRoue.get("/jwt", JWTRoute); * * @apiSuccess {String} public_key The applications public_key. Used to verify JWT. */ -OAuthRoue.get("/public", Public); +OAuthRoute.get("/public", Public); /** * @api {get} /oauth/refresh @@ -51,7 +52,7 @@ OAuthRoue.get("/public", Public); * * @apiGroup oauth */ -OAuthRoue.get("/refresh", RefreshTokenRoute); +OAuthRoute.get("/refresh", RefreshTokenRoute); /** * @api {post} /oauth/refresh @@ -59,5 +60,14 @@ OAuthRoue.get("/refresh", RefreshTokenRoute); * * @apiGroup oauth */ -OAuthRoue.post("/refresh", RefreshTokenRoute); -export default OAuthRoue; +OAuthRoute.post("/refresh", RefreshTokenRoute); + +/** + * @api {get} /oauth/profile + * @apiName OAuthProfile + * + * @apiGroup oauth + */ +OAuthRoute.get("/profile", ProfileRoute); + +export default OAuthRoute; diff --git a/Backend/src/api/oauth/profile.ts b/Backend/src/api/oauth/profile.ts new file mode 100644 index 0000000..3e0ea36 --- /dev/null +++ b/Backend/src/api/oauth/profile.ts @@ -0,0 +1,23 @@ +import Mail from "../../models/mail"; +import { GetClientApiAuthMiddleware } from "../middlewares/client"; +import Stacker from "../middlewares/stacker"; +import { Request, Response } from "express"; + +export default Stacker(GetClientApiAuthMiddleware(), async (req: Request, res) => { + let mails = await Promise.all( + req.user.mails.map((id) => Mail.findById(id)) + ); + + let mail = mails.find((e) => e.primary) || mails[0]; + + res.json({ + user_id: req.user.uid, + id: req.user.uid, + ID: req.user.uid, + sub: req.user.uid, + email: mail.mail, + username: req.user.username, + displayName: req.user.name, + displayNameClaim: req.user.name, + }); +}) diff --git a/Backend/src/testdata.ts b/Backend/src/testdata.ts index 957476e..2744316 100644 --- a/Backend/src/testdata.ts +++ b/Backend/src/testdata.ts @@ -101,7 +101,7 @@ export default async function TestData() { data: "IIRW2P2UJRDDO2LDIRYW4LSREZLWMOKDNBJES2LLHRREK3R6KZJQ", expires: null, }); - TwoFactor.save(t); + await TwoFactor.save(t); } let login_token = await LoginToken.findOne({ token: "test01" }); diff --git a/Backend/src/web.ts b/Backend/src/web.ts index aba2f00..2b636f7 100644 --- a/Backend/src/web.ts +++ b/Backend/src/web.ts @@ -63,8 +63,7 @@ export default class Web { let m = req.method; while (m.length < 4) m += " "; Logging.log( - `${m} ${req.originalUrl} ${ - (req as any).language || "" + `${m} ${req.originalUrl} ${(req as any).language || "" } ${resColor}${res.statusCode}\x1b[0m - ${time}ms` ); res.removeListener("finish", listener); @@ -107,7 +106,7 @@ export default class Web { if (error.status === 500 && !(error).nolog) { Logging.error(error); } else { - Logging.log("Responded with Error", error.status); + Logging.log("Responded with Error", error.status, error.message); } if (req.accepts(["json"])) { diff --git a/Frontend/.editorconfig b/Frontend/.editorconfig new file mode 100644 index 0000000..f8f5ab2 --- /dev/null +++ b/Frontend/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +indent_style = space +indent_size = 3 +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = true + +[*.svelte] +indent_size = 2 \ No newline at end of file diff --git a/Frontend/.gitignore b/Frontend/.gitignore new file mode 100644 index 0000000..a7b3c70 --- /dev/null +++ b/Frontend/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +node_modules +public/bundle.* +yarn.lock +.rpt2_cache +build/ +build.js +*.old \ No newline at end of file diff --git a/Frontend/README.md b/Frontend/README.md new file mode 100644 index 0000000..a1eb2a8 --- /dev/null +++ b/Frontend/README.md @@ -0,0 +1,68 @@ +_Psst �looking for a shareable component template? Go here --> [sveltejs/component-template](https://github.com/sveltejs/component-template)_ + +--- + +# svelte app + +This is a project template for [Svelte](https://svelte.technology) apps. It lives at https://github.com/sveltejs/template. + +To create a new project based on this template using [degit](https://github.com/Rich-Harris/degit): + +```bash +npm install -g degit # you only need to do this once + +degit sveltejs/template svelte-app +cd svelte-app +``` + +_Note that you will need to have [Node.js](https://nodejs.org) installed._ + +## Get started + +Install the dependencies... + +```bash +cd svelte-app +npm install +``` + +...then start [Rollup](https://rollupjs.org): + +```bash +npm run dev +``` + +Navigate to [localhost:5000](http://localhost:5000). You should see your app running. Edit a component file in `src`, save it, and reload the page to see your changes. + +## Deploying to the web + +### With [now](https://zeit.co/now) + +Install `now` if you haven't already: + +```bash +npm install -g now +``` + +Then, from within your project folder: + +```bash +now +``` + +As an alternative, use the [Now desktop client](https://zeit.co/download) and simply drag the unzipped project folder to the taskbar icon. + +### With [surge](https://surge.sh/) + +Install `surge` if you haven't already: + +```bash +npm install -g surge +``` + +Then, from within your project folder: + +```bash +npm run build +surge public +``` diff --git a/Frontend/index.js b/Frontend/index.js new file mode 100644 index 0000000..e69de29 diff --git a/Frontend/package.json b/Frontend/package.json new file mode 100644 index 0000000..23fdfb4 --- /dev/null +++ b/Frontend/package.json @@ -0,0 +1,36 @@ +{ + "name": "@hibas123/openauth-views-v2", + "main": "index.js", + "devDependencies": { + "@rollup/plugin-html": "^1.0.2", + "@rollup/plugin-image": "^3.0.2", + "@rollup/plugin-node-resolve": "^15.0.2", + "@tsconfig/svelte": "^4.0.1", + "@types/cleave.js": "^1.4.7", + "esbuild": "^0.17.15", + "postcss": "^8.4.21", + "postcss-import": "^15.1.0", + "postcss-url": "^10.1.3", + "rollup": "^3.20.2", + "rollup-plugin-esbuild": "^5.0.0", + "rollup-plugin-livereload": "^2.0.5", + "rollup-plugin-postcss": "^4.0.2", + "rollup-plugin-sizes": "^1.0.5", + "rollup-plugin-svelte": "^7.1.4", + "rollup-plugin-visualizer": "^5.9.0", + "svelte": "^3.58.0", + "svelte-preprocess": "^5.0.3", + "typescript": "^5.0.3" + }, + "scripts": { + "prepublishOnly": "npm run build", + "build": "rollup -c rollup.config.mjs ", + "dev": "rollup -c rollup.config.mjs -w" + }, + "dependencies": { + "@hibas123/theme": "^2.0.6", + "@hibas123/utils": "^2.2.18", + "cleave.js": "^1.6.0", + "what-the-pack": "^2.0.3" + } +} diff --git a/Frontend/postcss.config.js b/Frontend/postcss.config.js new file mode 100644 index 0000000..f913c37 --- /dev/null +++ b/Frontend/postcss.config.js @@ -0,0 +1,3 @@ +module.exports = { + plugins: [], +}; diff --git a/Frontend/rollup.config.mjs b/Frontend/rollup.config.mjs new file mode 100644 index 0000000..40b2aeb --- /dev/null +++ b/Frontend/rollup.config.mjs @@ -0,0 +1,123 @@ +import svelte from "rollup-plugin-svelte"; +import esbuild from "rollup-plugin-esbuild"; +import html from "@rollup/plugin-html"; +import resolve from "@rollup/plugin-node-resolve"; +import image from "@rollup/plugin-image"; +import sizes from "rollup-plugin-sizes"; +import { visualizer } from "rollup-plugin-visualizer"; +import postcss from "rollup-plugin-postcss"; +import livereload from "rollup-plugin-livereload"; +import sveltePreprocess from "svelte-preprocess"; + +const VIEWS = ["home", "login", "popup", "user"]; + +const dev = process.env.NODE_ENV !== "production"; + +const htmlTemplate = ({ attributes, meta, files, publicPath, title }) => { + const makeHtmlAttributes = (attributes) => { + if (!attributes) { + return ""; + } + + const keys = Object.keys(attributes); + // eslint-disable-next-line no-param-reassign + return keys.reduce( + (result, key) => (result += ` ${key}="${attributes[key]}"`), + "" + ); + }; + const scripts = (files.js || []) + .map(({ fileName }) => { + const attrs = makeHtmlAttributes(attributes.script); + return ``; + }) + .join("\n"); + + const links = (files.css || []) + .map(({ fileName }) => { + const attrs = makeHtmlAttributes(attributes.link); + return ``; + }) + .join("\n"); + + const metas = meta + .map((input) => { + const attrs = makeHtmlAttributes(input); + return ``; + }) + .join("\n"); + + return ` + + + + ${metas} + ${title} + + + ${links} + + + ${scripts} + +`; +}; + +export default VIEWS.map((view) => ({ + input: `src/pages/${view}/main.ts`, + output: [ + dev + ? { + file: `build/${view}/bundle.js`, + format: "iife", + sourcemap: true, + name: view, + } + : { + file: `build/${view}/bundle.min.js`, + format: "iife", + name: view, + plugins: [ + esbuild({ + minify: true, + }), + ], + }, + ], + plugins: [ + svelte({ + emitCss: true, + preprocess: sveltePreprocess({}), + }), + esbuild({ sourceMap: dev }), + html({ + title: view, + attributes: { + html: { lang: "en" }, + }, + meta: [ + { + name: "viewport", + content: "width=device-width", + }, + ], + template: htmlTemplate, + }), + resolve({ + browser: true, + dedupe: ["svelte"], + exportConditions: ["svelte"], + }), + image(), + sizes(), + visualizer({ + filename: `build/stats/${view}.html`, + title: `Rullup bundle for ${view}`, + }), + postcss({ + extract: `bundle.css`, //TODO: Check if it should be enabled + // inject: true, + }), + // dev && livereload(), + ], +})); diff --git a/Frontend/src/components/HoveringContentBox.svelte b/Frontend/src/components/HoveringContentBox.svelte new file mode 100644 index 0000000..e40a728 --- /dev/null +++ b/Frontend/src/components/HoveringContentBox.svelte @@ -0,0 +1,90 @@ + + + + +
+
+ +
+

{title}

+
+ {#if loading} +
+
+
+
+
+ {/if} + +
+ {#if !(loading && hide)} + + {/if} +
+ +
+
diff --git a/Frontend/src/components/theme/Theme.svelte b/Frontend/src/components/theme/Theme.svelte new file mode 100644 index 0000000..b6d31e5 --- /dev/null +++ b/Frontend/src/components/theme/Theme.svelte @@ -0,0 +1,15 @@ + + + +
+ +
diff --git a/Frontend/src/components/theme/index.ts b/Frontend/src/components/theme/index.ts new file mode 100644 index 0000000..df86d20 --- /dev/null +++ b/Frontend/src/components/theme/index.ts @@ -0,0 +1,42 @@ +import "@hibas123/theme/out/base.css"; +import "./theme.css"; +import { default as Theme } from "./Theme.svelte"; + +(() => { + const elements = new WeakSet(); + + function check() { + document + .querySelectorAll(".floating>input") + .forEach((e: HTMLInputElement) => { + if (elements.has(e)) return; + elements.add(e); + + function checkState() { + console.log("Check State"); + if (e.value !== "") { + if (e.classList.contains("used")) return; + e.classList.add("used"); + } else { + if (e.classList.contains("used")) e.classList.remove("used"); + } + } + + e.addEventListener("change", () => checkState()); + checkState(); + }); + } + + const observer = new MutationObserver((mutations) => { + check(); + }); + + // Start observing the target node for configured mutations + observer.observe(window.document, { + childList: true, + subtree: true, + }); + check(); +})(); + +export default Theme; diff --git a/Frontend/src/components/theme/theme.css b/Frontend/src/components/theme/theme.css new file mode 100644 index 0000000..2b8032f --- /dev/null +++ b/Frontend/src/components/theme/theme.css @@ -0,0 +1,251 @@ +:root { + --primary: #1e88e5; + --mdc-theme-primary: var(--primary); + --mdc-theme-primary-bg: var(--mdc-theme--primary); + --mdc-theme-on-primary: white; + --error: #ff2f00; + --border-color: #ababab; + + --default-font-size: 1.05rem; +} + +* { + font-family: "Roboto", "Helvetica", sans-serif; +} + +html, +body { + margin: 0; + color: #636363; + position: relative; + background: #eee; + height: 100%; + font-size: var(--default-font-size); + min-width: 100vw; + min-height: 100vh; + box-sizing: border-box; +} + +.group { + position: relative; + margin-bottom: 24px; + min-height: 45px; +} + +.floating > input { + font-size: 1.2rem; + padding: 10px 10px 10px 5px; + appearance: none; + -webkit-appearance: none; + display: block; + background: #fafafa; + background: unset; + color: #636363; + width: 100%; + border: none; + border-radius: 0; + /* border-bottom: 1px solid #757575; */ + border-bottom: 1px solid var(--border-color); + box-sizing: border-box; +} + +.floating > input:focus { + outline: none; +} + +/* Label */ + +.floating > label { + color: #999; + font-size: 18px; + font-weight: normal; + position: absolute; + pointer-events: none; + left: 5px; + top: 10px; + transition: all 0.2s ease; +} + +/* active */ + +.floating > input:focus ~ label, +.floating > input.used ~ label { + top: -0.75em; + transform: scale(0.75); + left: -2px; + /* font-size: 14px; */ + color: var(--primary); + transform-origin: left; +} + +/* Underline */ + +.bar { + position: relative; + display: block; + width: 100%; +} + +.bar:before, +.bar:after { + content: ""; + height: 2px; + width: 0; + bottom: 1px; + position: absolute; + background: var(--primary); + transition: all 0.2s ease; +} + +.bar:before { + left: 50%; +} + +.bar:after { + right: 50%; +} + +/* active */ + +.floating > input:focus ~ .bar:before, +.floating > input:focus ~ .bar:after { + width: 50%; +} + +/* Highlight */ + +.highlight { + position: absolute; + height: 60%; + width: 100px; + top: 25%; + left: 0; + pointer-events: none; + opacity: 0.5; +} + +/* active */ + +.floating > input:focus ~ .highlight { + animation: inputHighlighter 0.3s ease; +} + +/* Animations */ + +@keyframes inputHighlighter { + from { + background: var(--primary); + } + + to { + width: 0; + background: transparent; + } +} + +.btn { + position: relative; + + display: block; + margin: 2rem; + padding: 0 1em; + + overflow: hidden; + + border-width: 0; + outline: none; + border-radius: 4px; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.6); + + background-color: #cccccc; + color: #ecf0f1; + + transition: background-color 0.3s; + + height: 48px; + + text-transform: uppercase; + font-weight: 500; + font-size: 1.2rem; +} + +.btn:hover, +.btn:focus { + filter: brightness(90%); +} + +.btn > * { + position: relative; +} + +.btn span { + display: block; + padding: 12px 24px; +} + +.btn:before { + content: ""; + + position: absolute; + top: 50%; + left: 50%; + + display: block; + width: 0; + padding-top: 0; + + border-radius: 100%; + + background-color: rgba(236, 240, 241, 0.3); + + -webkit-transform: translate(-50%, -50%); + -moz-transform: translate(-50%, -50%); + -ms-transform: translate(-50%, -50%); + -o-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); +} + +.btn:active:before { + width: 120%; + padding-top: 120%; + + transition: width 0.2s ease-out, padding-top 0.2s ease-out; +} + +.loader_box { + width: 64px; + height: 64px; + margin: auto; +} + +.loader { + display: inline-block; + position: relative; + z-index: 100; +} + +.loader:after { + content: " "; + display: block; + width: 46px; + height: 46px; + margin: 1px; + border-radius: 50%; + border: 5px solid var(--primary); + border-color: var(--primary) transparent var(--primary) transparent; + animation: loader 1.2s linear infinite; +} + +@keyframes loader { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } +} + +#content { + height: 100%; +} diff --git a/Frontend/src/helper/cookie.ts b/Frontend/src/helper/cookie.ts new file mode 100644 index 0000000..5eee21e --- /dev/null +++ b/Frontend/src/helper/cookie.ts @@ -0,0 +1,20 @@ +export function setCookie(cname: string, cvalue: string, exdate: string) { + const expires = exdate ? `;expires=${exdate}` : ""; + document.cookie = `${cname}=${cvalue}${expires};path=/;`; +} + +export function getCookie(cname: string) { + const name = cname + "="; + const dc = decodeURIComponent(document.cookie); + const ca = dc.split(";"); + for (let i = 0; i < ca.length; i++) { + let c = ca[i]; + while (c.charAt(0) == " ") { + c = c.substring(1); + } + if (c.indexOf(name) == 0) { + return c.substring(name.length, c.length); + } + } + return ""; +} diff --git a/Frontend/src/helper/request.ts b/Frontend/src/helper/request.ts new file mode 100644 index 0000000..1d620b6 --- /dev/null +++ b/Frontend/src/helper/request.ts @@ -0,0 +1,53 @@ +import { getCookie } from "./cookie"; + +const baseURL = ""; + +export default async function request( + endpoint: string, + parameters: { [key: string]: string } = {}, + method: "GET" | "POST" | "DELETE" | "PUT" = "GET", + body?: any, + authInParam = false, + redirect = false +) { + let pairs = []; + + if (authInParam) { + parameters.login = getCookie("login"); + parameters.special = getCookie("special"); + } + + for (let key in parameters) { + pairs.push(key + "=" + parameters[key]); + } + + let url = endpoint; + if (pairs.length > 0) { + url += "?" + pairs.join("&"); + } + + return fetch(baseURL + url, { + method, + body: JSON.stringify(body), + credentials: "same-origin", + headers: { + "content-type": "application/json", + }, + }) + .then((e) => { + if (e.status !== 200) throw new Error(e.statusText); + return e.json(); + }) + .then((data) => { + if (data.error) { + if (redirect && data.additional && data.additional.auth) { + let state = btoa( + window.location.pathname + window.location.hash + ); + window.location.href = `/login?state=${state}&base64=true`; + } + return Promise.reject(new Error(data.error)); + } + return data; + }); +} diff --git a/Frontend/src/helper/sha512.js b/Frontend/src/helper/sha512.js new file mode 100644 index 0000000..a47137f --- /dev/null +++ b/Frontend/src/helper/sha512.js @@ -0,0 +1,484 @@ +var b; +if (!(b = t)) { + var w = Math, + y = {}, + B = (y.p = {}), + aa = function () {}, + C = (B.A = { + extend: function (o) { + aa.prototype = this; + var _ = new aa(); + return o && _.u(o), (_.z = this), _; + }, + create: function () { + var o = this.extend(); + return o.h.apply(o, arguments), o; + }, + h: function () {}, + u: function (o) { + for (var _ in o) o.hasOwnProperty(_) && (this[_] = o[_]); + o.hasOwnProperty("toString") && (this.toString = o.toString); + }, + e: function () { + return this.z.extend(this); + }, + }), + D = (B.i = C.extend({ + h: function (o, _) { + (o = this.d = o || []), (this.c = void 0 == _ ? 4 * o.length : _); + }, + toString: function (o) { + return (o || ba).stringify(this); + }, + concat: function (o) { + var _ = this.d, + Da = o.d, + Ea = this.c, + o = o.c; + if ((this.t(), Ea % 4)) + for (var Fa = 0; Fa < o; Fa++) + _[(Ea + Fa) >>> 2] |= + (255 & (Da[Fa >>> 2] >>> (24 - 8 * (Fa % 4)))) << + (24 - 8 * ((Ea + Fa) % 4)); + else if (65535 < Da.length) + for (Fa = 0; Fa < o; Fa += 4) _[(Ea + Fa) >>> 2] = Da[Fa >>> 2]; + else _.push.apply(_, Da); + return (this.c += o), this; + }, + t: function () { + var o = this.d, + _ = this.c; + (o[_ >>> 2] &= 4294967295 << (32 - 8 * (_ % 4))), + (o.length = w.ceil(_ / 4)); + }, + e: function () { + var o = C.e.call(this); + return (o.d = this.d.slice(0)), o; + }, + random: function (o) { + for (var _ = [], Da = 0; Da < o; Da += 4) + _.push(0 | (4294967296 * w.random())); + return D.create(_, o); + }, + })), + ca = (y.O = {}), + ba = (ca.K = { + stringify: function (o) { + for (var Fa, _ = o.d, o = o.c, Da = [], Ea = 0; Ea < o; Ea++) + (Fa = 255 & (_[Ea >>> 2] >>> (24 - 8 * (Ea % 4)))), + Da.push((Fa >>> 4).toString(16)), + Da.push((15 & Fa).toString(16)); + return Da.join(""); + }, + parse: function (o) { + for (var _ = o.length, Da = [], Ea = 0; Ea < _; Ea += 2) + Da[Ea >>> 3] |= + parseInt(o.substr(Ea, 2), 16) << (24 - 4 * (Ea % 8)); + return D.create(Da, _ / 2); + }, + }), + da = (ca.M = { + stringify: function (o) { + for (var _ = o.d, o = o.c, Da = [], Ea = 0; Ea < o; Ea++) + Da.push( + String.fromCharCode( + 255 & (_[Ea >>> 2] >>> (24 - 8 * (Ea % 4))) + ) + ); + return Da.join(""); + }, + parse: function (o) { + for (var _ = o.length, Da = [], Ea = 0; Ea < _; Ea++) + Da[Ea >>> 2] |= (255 & o.charCodeAt(Ea)) << (24 - 8 * (Ea % 4)); + return D.create(Da, _); + }, + }), + ea = (ca.N = { + stringify: function (o) { + try { + return decodeURIComponent(escape(da.stringify(o))); + } catch (_) { + throw Error("Malformed UTF-8 data"); + } + }, + parse: function (o) { + return da.parse(unescape(encodeURIComponent(o))); + }, + }), + ia = (B.I = C.extend({ + reset: function () { + (this.g = D.create()), (this.j = 0); + }, + l: function (o) { + "string" == typeof o && (o = ea.parse(o)), + this.g.concat(o), + (this.j += o.c); + }, + m: function (o) { + var _ = this.g, + Da = _.d, + Ea = _.c, + Fa = this.n, + Ga = Ea / (4 * Fa), + Ga = o ? w.ceil(Ga) : w.max((0 | Ga) - this.r, 0), + o = Ga * Fa, + Ea = w.min(4 * o, Ea); + if (o) { + for (var Ha = 0; Ha < o; Ha += Fa) this.H(Da, Ha); + (Ha = Da.splice(0, o)), (_.c -= Ea); + } + return D.create(Ha, Ea); + }, + e: function () { + var o = C.e.call(this); + return (o.g = this.g.e()), o; + }, + r: 0, + })); + B.B = ia.extend({ + h: function () { + this.reset(); + }, + reset: function () { + ia.reset.call(this), this.q(); + }, + update: function (o) { + return this.l(o), this.m(), this; + }, + o: function (o) { + return o && this.l(o), this.G(), this.f; + }, + e: function () { + var o = ia.e.call(this); + return (o.f = this.f.e()), o; + }, + n: 16, + D: function (o) { + return function (_, Da) { + return o.create(Da).o(_); + }; + }, + F: function (o) { + return function (_, Da) { + return ja.J.create(o, Da).o(_); + }; + }, + }); + var ja = (y.s = {}); + b = y; +} +var t = b, + K = t, + ka = K.p, + la = ka.A, + va = ka.i, + K = (K.w = {}); +(K.C = la.extend({ + h: function (o, _) { + (this.a = o), (this.b = _); + }, +})), + (K.i = la.extend({ + h: function (o, _) { + (o = this.d = o || []), (this.c = void 0 == _ ? 8 * o.length : _); + }, + v: function () { + for (var Fa, o = this.d, _ = o.length, Da = [], Ea = 0; Ea < _; Ea++) + (Fa = o[Ea]), Da.push(Fa.a), Da.push(Fa.b); + return va.create(Da, this.c); + }, + e: function () { + for ( + var o = la.e.call(this), + _ = (o.d = this.d.slice(0)), + Da = _.length, + Ea = 0; + Ea < Da; + Ea++ + ) + _[Ea] = _[Ea].e(); + return o; + }, + })); +function L() { + return wa.create.apply(wa, arguments); +} +for ( + var xa = t.p.B, + M = t.w, + wa = M.C, + ya = M.i, + M = t.s, + za = [ + L(1116352408, 3609767458), + L(1899447441, 602891725), + L(3049323471, 3964484399), + L(3921009573, 2173295548), + L(961987163, 4081628472), + L(1508970993, 3053834265), + L(2453635748, 2937671579), + L(2870763221, 3664609560), + L(3624381080, 2734883394), + L(310598401, 1164996542), + L(607225278, 1323610764), + L(1426881987, 3590304994), + L(1925078388, 4068182383), + L(2162078206, 991336113), + L(2614888103, 633803317), + L(3248222580, 3479774868), + L(3835390401, 2666613458), + L(4022224774, 944711139), + L(264347078, 2341262773), + L(604807628, 2007800933), + L(770255983, 1495990901), + L(1249150122, 1856431235), + L(1555081692, 3175218132), + L(1996064986, 2198950837), + L(2554220882, 3999719339), + L(2821834349, 766784016), + L(2952996808, 2566594879), + L(3210313671, 3203337956), + L(3336571891, 1034457026), + L(3584528711, 2466948901), + L(113926993, 3758326383), + L(338241895, 168717936), + L(666307205, 1188179964), + L(773529912, 1546045734), + L(1294757372, 1522805485), + L(1396182291, 2643833823), + L(1695183700, 2343527390), + L(1986661051, 1014477480), + L(2177026350, 1206759142), + L(2456956037, 344077627), + L(2730485921, 1290863460), + L(2820302411, 3158454273), + L(3259730800, 3505952657), + L(3345764771, 106217008), + L(3516065817, 3606008344), + L(3600352804, 1432725776), + L(4094571909, 1467031594), + L(275423344, 851169720), + L(430227734, 3100823752), + L(506948616, 1363258195), + L(659060556, 3750685593), + L(883997877, 3785050280), + L(958139571, 3318307427), + L(1322822218, 3812723403), + L(1537002063, 2003034995), + L(1747873779, 3602036899), + L(1955562222, 1575990012), + L(2024104815, 1125592928), + L(2227730452, 2716904306), + L(2361852424, 442776044), + L(2428436474, 593698344), + L(2756734187, 3733110249), + L(3204031479, 2999351573), + L(3329325298, 3815920427), + L(3391569614, 3928383900), + L(3515267271, 566280711), + L(3940187606, 3454069534), + L(4118630271, 4000239992), + L(116418474, 1914138554), + L(174292421, 2731055270), + L(289380356, 3203993006), + L(460393269, 320620315), + L(685471733, 587496836), + L(852142971, 1086792851), + L(1017036298, 365543100), + L(1126000580, 2618297676), + L(1288033470, 3409855158), + L(1501505948, 4234509866), + L(1607167915, 987167468), + L(1816402316, 1246189591), + ], + $ = [], + Aa = 0; + 80 > Aa; + Aa++ +) + $[Aa] = L(); +(M = M.k = xa.extend({ + q: function () { + this.f = ya.create([ + L(1779033703, 4089235720), + L(3144134277, 2227873595), + L(1013904242, 4271175723), + L(2773480762, 1595750129), + L(1359893119, 2917565137), + L(2600822924, 725511199), + L(528734635, 4215389547), + L(1541459225, 327033209), + ]); + }, + H: function (o, _) { + for ( + var qb, + Da = this.f.d, + Ea = Da[0], + Fa = Da[1], + Ga = Da[2], + Ha = Da[3], + Ia = Da[4], + Ja = Da[5], + Ka = Da[6], + Da = Da[7], + La = Ea.a, + Ma = Ea.b, + Na = Fa.a, + Oa = Fa.b, + Pa = Ga.a, + Qa = Ga.b, + Ra = Ha.a, + Sa = Ha.b, + Ta = Ia.a, + Ua = Ia.b, + Va = Ja.a, + Wa = Ja.b, + Xa = Ka.a, + Ya = Ka.b, + Za = Da.a, + $a = Da.b, + _a = La, + ab = Ma, + bb = Na, + cb = Oa, + db = Pa, + eb = Qa, + fb = Ra, + gb = Sa, + hb = Ta, + ib = Ua, + jb = Va, + kb = Wa, + lb = Xa, + mb = Ya, + nb = Za, + ob = $a, + pb = 0; + 80 > pb; + pb++ + ) { + if (((qb = $[pb]), 16 > pb)) + var rb = (qb.a = 0 | o[_ + 2 * pb]), + sb = (qb.b = 0 | o[_ + 2 * pb + 1]); + else { + var rb = $[pb - 15], + sb = rb.a, + tb = rb.b, + rb = + ((tb << 31) | (sb >>> 1)) ^ + ((tb << 24) | (sb >>> 8)) ^ + (sb >>> 7), + tb = + ((sb << 31) | (tb >>> 1)) ^ + ((sb << 24) | (tb >>> 8)) ^ + ((sb << 25) | (tb >>> 7)), + ub = $[pb - 2], + sb = ub.a, + vb = ub.b, + ub = + ((vb << 13) | (sb >>> 19)) ^ + ((sb << 3) | (vb >>> 29)) ^ + (sb >>> 6), + vb = + ((sb << 13) | (vb >>> 19)) ^ + ((vb << 3) | (sb >>> 29)) ^ + ((sb << 26) | (vb >>> 6)), + sb = $[pb - 7], + wb = sb.a, + xb = $[pb - 16], + yb = xb.a, + xb = xb.b, + sb = tb + sb.b, + rb = rb + wb + (sb >>> 0 < tb >>> 0 ? 1 : 0), + sb = sb + vb, + rb = rb + ub + (sb >>> 0 < vb >>> 0 ? 1 : 0), + sb = sb + xb, + rb = rb + yb + (sb >>> 0 < xb >>> 0 ? 1 : 0); + (qb.a = rb), (qb.b = sb); + } + var wb = (hb & jb) ^ (~hb & lb), + xb = (ib & kb) ^ (~ib & mb), + qb = (_a & bb) ^ (_a & db) ^ (bb & db), + tb = + ((ab << 4) | (_a >>> 28)) ^ + ((_a << 30) | (ab >>> 2)) ^ + ((_a << 25) | (ab >>> 7)), + ub = + ((_a << 4) | (ab >>> 28)) ^ + ((ab << 30) | (_a >>> 2)) ^ + ((ab << 25) | (_a >>> 7)), + vb = za[pb], + Ab = vb.a, + Bb = vb.b, + vb = + ob + + (((hb << 18) | (ib >>> 14)) ^ + ((hb << 14) | (ib >>> 18)) ^ + ((ib << 23) | (hb >>> 9))), + yb = + nb + + (((ib << 18) | (hb >>> 14)) ^ + ((ib << 14) | (hb >>> 18)) ^ + ((hb << 23) | (ib >>> 9))) + + (vb >>> 0 < ob >>> 0 ? 1 : 0), + vb = vb + xb, + yb = yb + wb + (vb >>> 0 < xb >>> 0 ? 1 : 0), + vb = vb + Bb, + yb = yb + Ab + (vb >>> 0 < Bb >>> 0 ? 1 : 0), + vb = vb + sb, + yb = yb + rb + (vb >>> 0 < sb >>> 0 ? 1 : 0), + sb = ub + ((ab & cb) ^ (ab & eb) ^ (cb & eb)), + qb = tb + qb + (sb >>> 0 < ub >>> 0 ? 1 : 0), + nb = lb, + ob = mb, + lb = jb, + mb = kb, + jb = hb, + kb = ib, + ib = 0 | (gb + vb), + hb = 0 | (fb + yb + (ib >>> 0 < gb >>> 0 ? 1 : 0)), + fb = db, + gb = eb, + db = bb, + eb = cb, + bb = _a, + cb = ab, + ab = 0 | (vb + sb), + _a = 0 | (yb + qb + (ab >>> 0 < vb >>> 0 ? 1 : 0)); + } + (Ma = Ea.b = 0 | (Ma + ab)), + (Ea.a = 0 | (La + _a + (Ma >>> 0 < ab >>> 0 ? 1 : 0))), + (Oa = Fa.b = 0 | (Oa + cb)), + (Fa.a = 0 | (Na + bb + (Oa >>> 0 < cb >>> 0 ? 1 : 0))), + (Qa = Ga.b = 0 | (Qa + eb)), + (Ga.a = 0 | (Pa + db + (Qa >>> 0 < eb >>> 0 ? 1 : 0))), + (Sa = Ha.b = 0 | (Sa + gb)), + (Ha.a = 0 | (Ra + fb + (Sa >>> 0 < gb >>> 0 ? 1 : 0))), + (Ua = Ia.b = 0 | (Ua + ib)), + (Ia.a = 0 | (Ta + hb + (Ua >>> 0 < ib >>> 0 ? 1 : 0))), + (Wa = Ja.b = 0 | (Wa + kb)), + (Ja.a = 0 | (Va + jb + (Wa >>> 0 < kb >>> 0 ? 1 : 0))), + (Ya = Ka.b = 0 | (Ya + mb)), + (Ka.a = 0 | (Xa + lb + (Ya >>> 0 < mb >>> 0 ? 1 : 0))), + ($a = Da.b = 0 | ($a + ob)), + (Da.a = 0 | (Za + nb + ($a >>> 0 < ob >>> 0 ? 1 : 0))); + }, + G: function () { + var o = this.g, + _ = o.d, + Da = 8 * this.j, + Ea = 8 * o.c; + (_[Ea >>> 5] |= 128 << (24 - (Ea % 32))), + (_[(((Ea + 128) >>> 10) << 5) + 31] = Da), + (o.c = 4 * _.length), + this.m(), + (this.f = this.f.v()); + }, + n: 32, +})), + (t.k = xa.D(M)), + (t.L = xa.F(M)); +export default function sha512(o) { + return t.k(o) + ""; +} diff --git a/Frontend/src/pages/home/App.svelte b/Frontend/src/pages/home/App.svelte new file mode 100644 index 0000000..0e90ae8 --- /dev/null +++ b/Frontend/src/pages/home/App.svelte @@ -0,0 +1,44 @@ + + +
+

Home Page

+ +

About

+

+ OpenAuth is a Service to provide simple Authentication to a veriaty of + Applications. With a simple to use API and different Strategies, it can be + easily integrated into most Applications. +

+ +

QickLinks

+

+ If you want to manage your Account, click + here +

+ +

Applications using OpenAuth

+ + +
diff --git a/Frontend/src/pages/home/main.ts b/Frontend/src/pages/home/main.ts new file mode 100644 index 0000000..909de44 --- /dev/null +++ b/Frontend/src/pages/home/main.ts @@ -0,0 +1,8 @@ +import "../../components/theme"; +import App from "./App.svelte"; + +const app = new App({ + target: document.body, +}); + +export default app; diff --git a/Frontend/src/pages/login/App.svelte b/Frontend/src/pages/login/App.svelte new file mode 100644 index 0000000..9c6f09f --- /dev/null +++ b/Frontend/src/pages/login/App.svelte @@ -0,0 +1,124 @@ + + + + + + +
+ {#if state === states.redirect} + + {:else if state === states.credentials} + (loading = s)} /> + {:else if state === states.twofactor} + (loading = s)} /> + {/if} + +
+
+

Powered by {appname}

+
+
diff --git a/Frontend/src/pages/login/Credentials.svelte b/Frontend/src/pages/login/Credentials.svelte new file mode 100644 index 0000000..d6613c1 --- /dev/null +++ b/Frontend/src/pages/login/Credentials.svelte @@ -0,0 +1,84 @@ + + + + +{#if state === states.username} +

Enter your Username or your E-Mail Address

+
+ + + + +
{error}
+
+{:else} +

Enter password for {username}

+
+ + + + +
{error}
+
+{/if} + + diff --git a/Frontend/src/pages/login/Redirect.svelte b/Frontend/src/pages/login/Redirect.svelte new file mode 100644 index 0000000..9e4990b --- /dev/null +++ b/Frontend/src/pages/login/Redirect.svelte @@ -0,0 +1,99 @@ + + + + +
+ + + + +
+ +

{text}

+ diff --git a/Frontend/src/pages/login/Twofactor.svelte b/Frontend/src/pages/login/Twofactor.svelte new file mode 100644 index 0000000..f329c25 --- /dev/null +++ b/Frontend/src/pages/login/Twofactor.svelte @@ -0,0 +1,104 @@ + + + + +
+ {#if !twofactor} +

Select your Authentication method:

+
    + {#each twofactors as tf} +
  • (twofactor = tf)}> +
    + +
    + +
    {tf.name}
    +
  • + {/each} +
+ {:else if twofactor.type === TFATypes.OTC} + + {:else if twofactor.type === TFATypes.BACKUP_CODE} + + {:else if twofactor.type === TFATypes.U2F} + + {:else if twofactor.type === TFATypes.APP_ALLOW} + + {:else} +
Invalid TwoFactor Method!
+ {/if} + +
diff --git a/Frontend/src/pages/login/api.ts b/Frontend/src/pages/login/api.ts new file mode 100644 index 0000000..0d13cb6 --- /dev/null +++ b/Frontend/src/pages/login/api.ts @@ -0,0 +1,182 @@ +import request from "../../helper/request"; +import sha from "../../helper/sha512"; +import { setCookie, getCookie } from "../../helper/cookie"; + +export interface TwoFactor { + id: string; + name: string; + type: TFATypes; +} + +export enum TFATypes { + OTC, + BACKUP_CODE, + U2F, + APP_ALLOW, +} + +// const Api = { +// // twofactor: [{ +// // id: "1", +// // name: "Backup Codes", +// // type: TFATypes.BACKUP_CODE +// // }, { +// // id: "2", +// // name: "YubiKey", +// // type: TFATypes.U2F +// // }, { +// // id: "3", +// // name: "Authenticator", +// // type: TFATypes.OTC +// // }] as TwoFactor[], + +// } + +export interface IToken { + token: string; + expires: string; +} + +function makeid(length) { + var result = ""; + var characters = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + var charactersLength = characters.length; + for (var i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; +} + +export default class Api { + static salt: string; + static login: IToken; + static special: IToken; + static username: string; + + static twofactor: any[]; + + static getUsername() { + return this.username || getCookie("username"); + } + + static async setUsername( + username: string + ): Promise<{ error: string | undefined }> { + return request( + "/api/user/login", + { + type: "username", + username, + }, + "POST" + ) + .then((res) => { + this.salt = res.salt; + this.username = username; + return { + error: undefined, + }; + }) + .catch((err) => { + let error = err.message; + return { error }; + }); + } + + static async setPassword( + password: string + ): Promise<{ error: string | undefined; twofactor?: any }> { + const date = new Date().valueOf(); + let pw = sha(sha(this.salt + password) + date.toString()); + return request( + "/api/user/login", + { + type: "password", + }, + "POST", + { + username: this.username, + password: pw, + date, + } + ) + .then(({ login, special, tfa }) => { + this.login = login; + this.special = special; + + if (tfa && Array.isArray(tfa) && tfa.length > 0) + this.twofactor = tfa; + else this.twofactor = undefined; + + return { + error: undefined, + }; + }) + .catch((err) => { + let error = err.message; + return { error }; + }); + } + + static gettok() { + return { + login: this.login.token, + special: this.special.token, + }; + } + + static async sendBackup(id: string, code: string) { + return request("/api/user/twofactor/backup", this.gettok(), "PUT", { + code, + id, + }) + .then(({ login_exp, special_exp }) => { + this.login.expires = login_exp; + this.special.expires = special_exp; + return {}; + }) + .catch((err) => ({ error: err.message })); + } + + static async sendOTC(id: string, code: string) { + return request("/api/user/twofactor/otc", this.gettok(), "PUT", { + code, + id, + }) + .then(({ login_exp, special_exp }) => { + this.login.expires = login_exp; + this.special.expires = special_exp; + return {}; + }) + .catch((error) => ({ error: error.message })); + } + + static finish() { + let d = new Date(); + d.setTime(d.getTime() + 30 * 24 * 60 * 60 * 1000); //Keep the username 30 days + setCookie("username", this.username, d.toUTCString()); + + setCookie( + "login", + this.login.token, + new Date(this.login.expires).toUTCString() + ); + setCookie( + "special", + this.special.token, + new Date(this.special.expires).toUTCString() + ); + + let url = new URL(window.location.href); + let state = url.searchParams.get("state"); + let red = "/"; + + if (state) { + let base64 = url.searchParams.get("base64"); + if (base64) red = atob(state); + else red = state; + } + setTimeout(() => (window.location.href = red), 200); + } +} diff --git a/Frontend/src/pages/login/icons/AppPush.svg b/Frontend/src/pages/login/icons/AppPush.svg new file mode 100644 index 0000000..dfb7ce3 --- /dev/null +++ b/Frontend/src/pages/login/icons/AppPush.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Frontend/src/pages/login/icons/Authenticator.svg b/Frontend/src/pages/login/icons/Authenticator.svg new file mode 100644 index 0000000..9afabf6 --- /dev/null +++ b/Frontend/src/pages/login/icons/Authenticator.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Frontend/src/pages/login/icons/BackupCode.svg b/Frontend/src/pages/login/icons/BackupCode.svg new file mode 100644 index 0000000..8cfd305 --- /dev/null +++ b/Frontend/src/pages/login/icons/BackupCode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Frontend/src/pages/login/icons/Icon.svelte b/Frontend/src/pages/login/icons/Icon.svelte new file mode 100644 index 0000000..dad45d4 --- /dev/null +++ b/Frontend/src/pages/login/icons/Icon.svelte @@ -0,0 +1,15 @@ + + +{#if icon_name === "SecurityKey"} + +{:else if icon_name === "Authenticator"} + +{:else if icon_name === "BackupCode"} + +{:else if icon_name === "AppPush"} + +{:else} +ERR +{/if} \ No newline at end of file diff --git a/Frontend/src/pages/login/icons/SecurityKey.svg b/Frontend/src/pages/login/icons/SecurityKey.svg new file mode 100644 index 0000000..255cb8b --- /dev/null +++ b/Frontend/src/pages/login/icons/SecurityKey.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Frontend/src/pages/login/main.ts b/Frontend/src/pages/login/main.ts new file mode 100644 index 0000000..26a5259 --- /dev/null +++ b/Frontend/src/pages/login/main.ts @@ -0,0 +1,5 @@ +import App from "./App.svelte"; + +new App({ + target: document.body, +}); diff --git a/Frontend/src/pages/login/twofactors/codeInput.svelte b/Frontend/src/pages/login/twofactors/codeInput.svelte new file mode 100644 index 0000000..7b1bec2 --- /dev/null +++ b/Frontend/src/pages/login/twofactors/codeInput.svelte @@ -0,0 +1,33 @@ + + + + +
+ + + + +
{error}
+
diff --git a/Frontend/src/pages/login/twofactors/otc.svelte b/Frontend/src/pages/login/twofactors/otc.svelte new file mode 100644 index 0000000..dadf109 --- /dev/null +++ b/Frontend/src/pages/login/twofactors/otc.svelte @@ -0,0 +1,50 @@ + + + + +

{title}

+ + + +
+ + +
diff --git a/Frontend/src/pages/login/twofactors/push.svelte b/Frontend/src/pages/login/twofactors/push.svelte new file mode 100644 index 0000000..1bd7b4e --- /dev/null +++ b/Frontend/src/pages/login/twofactors/push.svelte @@ -0,0 +1,389 @@ + + + + +

SMS

+ +

A code was sent to your Device {device}

+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
{error}
+ diff --git a/Frontend/src/pages/login/twofactors/sms.svelte b/Frontend/src/pages/login/twofactors/sms.svelte new file mode 100644 index 0000000..8c86d60 --- /dev/null +++ b/Frontend/src/pages/login/twofactors/sms.svelte @@ -0,0 +1,49 @@ + + + + +

SMS

+{#if state === states.approve} +

Send SMS to {number}

+ +{:else} +

A code was sent to you. Please enter

+ + +
+ (state = states.approve)}> + Not received? + +{/if} +
{error}
+ + diff --git a/Frontend/src/pages/login/twofactors/toList.svelte b/Frontend/src/pages/login/twofactors/toList.svelte new file mode 100644 index 0000000..11905d9 --- /dev/null +++ b/Frontend/src/pages/login/twofactors/toList.svelte @@ -0,0 +1,17 @@ + + + + +

+ evt.preventDefault() || finish(false)}> + Choose another Method + +

diff --git a/Frontend/src/pages/login/twofactors/u2f.svelte b/Frontend/src/pages/login/twofactors/u2f.svelte new file mode 100644 index 0000000..58d26ac --- /dev/null +++ b/Frontend/src/pages/login/twofactors/u2f.svelte @@ -0,0 +1,69 @@ + + + + +

U2F Security Key

+

This Method is currently not supported. Please choose another one!

+ diff --git a/Frontend/src/pages/popup/App.svelte b/Frontend/src/pages/popup/App.svelte new file mode 100644 index 0000000..b14241c --- /dev/null +++ b/Frontend/src/pages/popup/App.svelte @@ -0,0 +1,59 @@ + + + + +
+

+ Grant + {appName} + the following permissions? +

+
+ +
    + {#each view_perms as permission (permission._íd)} +
  • +

    {permission.name}

    +

    {permission.description}

    +
  • + {/each} +
+ +
+
+ + +
+
+
+
+ + diff --git a/Frontend/src/pages/popup/main.ts b/Frontend/src/pages/popup/main.ts new file mode 100644 index 0000000..579b373 --- /dev/null +++ b/Frontend/src/pages/popup/main.ts @@ -0,0 +1,146 @@ +import "../../components/theme"; +import App from "./App.svelte"; +import request from "../../helper/request"; + +interface IPermission { + _id: string; + name: string; + description: string; +} + +let loading = true; +let appName: string; +let permissions: IPermission[] = []; +let accept: () => void; + +const app = new App({ + target: document.body, + props: { loading, accept }, +}); + +const setLoading = (_loading: boolean) => { + loading = _loading; + app.$set({ loading }); +}; + +const setAppName = (_appName: string) => { + appName = _appName; + app.$set({ appName }); +}; + +const setPermissions = (_permissions: IPermission[]) => { + permissions = _permissions; + app.$set({ permissions }); +}; + +const setAccept = (_accept: () => void) => { + accept = _accept; + app.$set({ accept }); +}; + +async function getJWT(client_id: string, origin: string) { + origin = encodeURIComponent(origin); + client_id = encodeURIComponent(client_id); + + const res = await request(`/api/user/oauth/jwt`, { + client_id, + origin, + }); + + return res; +} + +async function getRefreshToken( + client_id: string, + origin: string, + permissions: string[] +) { + origin = encodeURIComponent(origin); + client_id = encodeURIComponent(client_id); + const perm = permissions.map((e) => encodeURIComponent(e)).join(","); + + const res = await request(`/api/user/oauth/refresh_token`, { + client_id, + origin, + permissions: perm, + }); + + return res; +} + +let started = false; +async function onMessage(msg: MessageEvent) { + const sendResponse = (data: any) => { + try { + console.log("Sending response:", data); + (msg.source.postMessage as any)(data, msg.origin); + } catch (err) { + alert("Something went wrong, please try again later!"); + } + }; + console.log("Received message", msg, started); + if (!started) { + started = true; + const url = new URL(msg.origin); + setAppName(url.hostname); + + try { + if (!msg.data.type || msg.data.type === "jwt") { + console.log("JWT Request"); + await new Promise((yes) => { + console.log("Await user acceptance"); + setLoading(false); + setAccept(yes); + }); + console.log("User has accepted"); + const res = await getJWT(msg.data.client_id, url.hostname); + sendResponse(res); + } else if (msg.data.type === "refresh") { + console.log("RefreshToken Request"); + let permissions = msg.data.permissions || []; + let permissions_resolved = []; + + if (permissions.length > 0) { + permissions_resolved = await request( + "/api/user/oauth/permissions", + { + client_id: msg.data.client_id, + origin: url.hostname, + permissions: permissions.join(","), + } + ).then(({ permissions }) => permissions); + } + + await new Promise((yes) => { + console.log("Await user acceptance"); + setLoading(false); + setPermissions(permissions_resolved); + setAccept(yes); + }); + + console.log("User has accepted"); + + const res = await getRefreshToken( + msg.data.client_id, + url.hostname, + permissions + ); + sendResponse(res); + } + } catch (err) { + sendResponse({ error: true, message: err.message }); + } + window.close(); + } +} + +setTimeout(() => { + if (!started) { + console.log("No authentication request received!"); + alert( + "The site requesting the login does not respond. Please try again later" + ); + } +}, 10000); + +window.addEventListener("message", onMessage); diff --git a/Frontend/src/pages/user/App.svelte b/Frontend/src/pages/user/App.svelte new file mode 100644 index 0000000..835f487 --- /dev/null +++ b/Frontend/src/pages/user/App.svelte @@ -0,0 +1,207 @@ + + +
+
+
+ {#if sidebar_button} + + {/if} +

{page.title}

+
+ +
+ +
+ +
+ +{#if loading} +
+
+
+
+
+{/if} + + diff --git a/Frontend/src/pages/user/NavigationBar.svelte b/Frontend/src/pages/user/NavigationBar.svelte new file mode 100644 index 0000000..5ce2727 --- /dev/null +++ b/Frontend/src/pages/user/NavigationBar.svelte @@ -0,0 +1,54 @@ + + +{#each pages as page} +
open(page.id)} + > +
+ {page.title} +
+

{page.title}

+
+{/each} + + diff --git a/Frontend/src/pages/user/Pages/Account.svelte b/Frontend/src/pages/user/Pages/Account.svelte new file mode 100644 index 0000000..0e9b26e --- /dev/null +++ b/Frontend/src/pages/user/Pages/Account.svelte @@ -0,0 +1,192 @@ + + + + + +

Profile

+ {#if account_error} +

{account_error}

+ {/if} + +
+
+ + + + +
+ +
+
+ +
+
+ +
+ +
+
+ + +
+ + +

Contact

+ {#if contact_error} +

{contact_error}

+ {/if} + + +
diff --git a/Frontend/src/pages/user/Pages/Box.svelte b/Frontend/src/pages/user/Pages/Box.svelte new file mode 100644 index 0000000..a656dd5 --- /dev/null +++ b/Frontend/src/pages/user/Pages/Box.svelte @@ -0,0 +1,36 @@ + + +
+ +
\ No newline at end of file diff --git a/Frontend/src/pages/user/Pages/BoxItem.svelte b/Frontend/src/pages/user/Pages/BoxItem.svelte new file mode 100644 index 0000000..6ea24fd --- /dev/null +++ b/Frontend/src/pages/user/Pages/BoxItem.svelte @@ -0,0 +1,94 @@ + + + + +
+
(open = !open)}> +
+
{name}
+
+ {#if Array.isArray(value)} + {#each value as v, i} + {v} + {#if i < value.length - 1} +
+ {/if} + {/each} + {:else}{value}{/if} +
+
+ {#if !noOpen} + + {/if} +
+ {#if open && !noOpen} +
+ +
+ {/if} +
diff --git a/Frontend/src/pages/user/Pages/NextIcon.svelte b/Frontend/src/pages/user/Pages/NextIcon.svelte new file mode 100644 index 0000000..0ba1da6 --- /dev/null +++ b/Frontend/src/pages/user/Pages/NextIcon.svelte @@ -0,0 +1,13 @@ + + + + + + + + + \ No newline at end of file diff --git a/Frontend/src/pages/user/Pages/Security.svelte b/Frontend/src/pages/user/Pages/Security.svelte new file mode 100644 index 0000000..85f89e1 --- /dev/null +++ b/Frontend/src/pages/user/Pages/Security.svelte @@ -0,0 +1,188 @@ + + + + + + + +

Two Factor

+ + {#each twofactor as t} + + + + {/each} + +
+ + +

Anmeldungen

+ + {#each token as t} + + + + {:else}No Tokens{/each} + + +
diff --git a/Frontend/src/pages/user/main.ts b/Frontend/src/pages/user/main.ts new file mode 100644 index 0000000..e6d3925 --- /dev/null +++ b/Frontend/src/pages/user/main.ts @@ -0,0 +1,6 @@ +import "../../components/theme"; +import App from "./App.svelte"; + +new App({ + target: document.body, +}); diff --git a/Frontend/svelte.config.js b/Frontend/svelte.config.js new file mode 100644 index 0000000..9e8c37a --- /dev/null +++ b/Frontend/svelte.config.js @@ -0,0 +1,5 @@ +const preprocess = require("svelte-preprocess"); + +module.exports = { + preprocess: preprocess({}), +}; diff --git a/Frontend/tsconfig.json b/Frontend/tsconfig.json new file mode 100644 index 0000000..3be68b1 --- /dev/null +++ b/Frontend/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "@tsconfig/svelte/tsconfig.json", + + "include": ["src/**/*", "src/node_modules"] +} diff --git a/FrontendLegacy/package.json b/FrontendLegacy/package.json index 4b692e1..3c7c004 100644 --- a/FrontendLegacy/package.json +++ b/FrontendLegacy/package.json @@ -1,6 +1,5 @@ { "name": "@hibas123/openauth-views-v1", - "version": "1.0.0", "main": "index.js", "author": "Fabian Stamm ", "license": "MIT", diff --git a/package.json b/package.json index 75728d4..3c404d6 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "name": "@hibas123/openauth", + "version": "1.2.0", "author": "Fabian Stamm ", "private": true, "scripts": {