keycloak-modern-login/.eslintcache

1 line
22 KiB
Plaintext

[{"/home/oliver/git-repos/keycloak-theme-vuejs/src/components/ErrorBox.vue":"1","/home/oliver/git-repos/keycloak-theme-vuejs/src/components/Layout.vue":"2","/home/oliver/git-repos/keycloak-theme-vuejs/src/shims-vue.d.ts":"3","/home/oliver/git-repos/keycloak-theme-vuejs/src/types/context.ts":"4","/home/oliver/git-repos/keycloak-theme-vuejs/src/views/login/index.ts":"5","/home/oliver/git-repos/keycloak-theme-vuejs/src/views/login/index.vue":"6","/home/oliver/git-repos/keycloak-theme-vuejs/src/views/webauthn-authenticate/index.ts":"7","/home/oliver/git-repos/keycloak-theme-vuejs/src/views/webauthn-authenticate/index.vue":"8","/home/oliver/git-repos/keycloak-theme-vuejs/tailwind.config.js":"9","/home/oliver/git-repos/keycloak-theme-vuejs/webpack.config.js":"10"},{"size":334,"mtime":1676764735273,"results":"11","hashOfConfig":"12"},{"size":703,"mtime":1676744043737,"results":"13","hashOfConfig":"12"},{"size":146,"mtime":1676768217528,"results":"14","hashOfConfig":"12"},{"size":10105,"mtime":1676767602409,"results":"15","hashOfConfig":"12"},{"size":155,"mtime":1676744043933,"results":"16","hashOfConfig":"12"},{"size":3456,"mtime":1676764735457,"results":"17","hashOfConfig":"12"},{"size":155,"mtime":1676744043997,"results":"18","hashOfConfig":"12"},{"size":6913,"mtime":1676768178300,"results":"19","hashOfConfig":"12"},{"size":144,"mtime":1676744044069,"results":"20","hashOfConfig":"12"},{"size":3277,"mtime":1676767942024,"results":"21","hashOfConfig":"12"},{"filePath":"22","messages":"23","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"yepsnx",{"filePath":"24","messages":"25","errorCount":2,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"26"},{"filePath":"27","messages":"28","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"29","messages":"30","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"31","messages":"32","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"33","messages":"34","errorCount":5,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"35"},{"filePath":"36","messages":"37","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"38","messages":"39","errorCount":12,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"40"},{"filePath":"41","messages":"42","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"43","messages":"44","errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"45"},"/home/oliver/git-repos/keycloak-theme-vuejs/src/components/ErrorBox.vue",[],"/home/oliver/git-repos/keycloak-theme-vuejs/src/components/Layout.vue",["46","47"],"<template>\n <div\n :class=\"\n 'flex flex-col min-h-screen w-full md:w-1/2 lg:w-1/3 max-w-none md:max-w-2xl bg-gray-50 md:rounded-r-3xl shadow-2xl bg-[url(\\'' +\n context.url.resourcesPath +\n '/img/background.jpg\\')] bg-no-repeat bg-center bg-cover font-sans overflow-hidden'\n \">\n <div class=\"flex flex-grow flex-col justify-center w-full px-8 md:px-20\">\n <slot></slot>\n </div>\n </div>\n</template>\n<script lang=\"ts\">\nimport { defineComponent } from \"vue\";\nimport { KcContextBase } from \"~/types/context\";\n\nexport default defineComponent({\n name: \"Layout\",\n data() {\n return {\n context: (window as any).kcContext as KcContextBase.Common,\n };\n },\n});\n</script>\n","/home/oliver/git-repos/keycloak-theme-vuejs/src/shims-vue.d.ts",[],"/home/oliver/git-repos/keycloak-theme-vuejs/src/types/context.ts",[],"/home/oliver/git-repos/keycloak-theme-vuejs/src/views/login/index.ts",[],"/home/oliver/git-repos/keycloak-theme-vuejs/src/views/login/index.vue",["48","49","50","51","52"],"<template>\n <layout>\n <h1 class=\"mb-8 text-3xl font-semibold text-center text-gray-700\">\n Login to your account\n </h1>\n <p\n v-if=\"context.realm.registrationAllowed\"\n class=\"mt-4 text-sm font-light text-center text-gray-700\">\n Don't have an account yet?\n <a\n :href=\"context.url.registrationUrl\"\n class=\"font-medium text-blue-500 hover:underline\"\n >Sign up</a\n >\n </p>\n <div v-if=\"context.message?.type == 'error'\">\n <ErrorBox>{{ context.message?.summary }}</ErrorBox>\n </div>\n <form\n class=\"flex flex-col justify-center mt-6\"\n :action=\"context.url.loginAction\"\n method=\"post\">\n <div>\n <label for=\"username\" class=\"block text-sm text-gray-800\">{{\n context.realm.loginWithEmailAllowed ? \"Email or Username\" : \"Username\"\n }}</label>\n <input\n name=\"username\"\n type=\"text\"\n :placeholder=\"\n context.realm.loginWithEmailAllowed\n ? 'jane.doe@example.com'\n : 'JDoe'\n \"\n :value=\"context.login.username ? context.login.username : ''\"\n class=\"block w-full px-4 py-2 mt-2 text-gray-800 bg-white border rounded-md focus:border-blue-500 focus:ring-transparent focus:outline-none focus:ring focus:ring-opacity-40\" />\n </div>\n <div class=\"mt-4\">\n <div>\n <label for=\"password\" class=\"block text-sm text-gray-800\"\n >Password</label\n >\n <input\n type=\"password\"\n name=\"password\"\n placeholder=\"&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;\"\n class=\"block w-full px-4 py-2 mt-2 text-gray-800 bg-white border rounded-md focus:border-blue-500 focus:ring-transparent focus:outline-none focus:ring focus:ring-opacity-40\" />\n </div>\n <a\n v-if=\"context.realm.resetPasswordAllowed\"\n :href=\"context.url.loginResetCredentialsUrl\"\n class=\"text-xs text-gray-600 hover:underline\"\n >Forgot Password?</a\n >\n </div>\n <div\n v-if=\"context.realm.rememberMe\"\n class=\"mt-5 flex flex-row items-center\">\n <input\n type=\"checkbox\"\n name=\"rememberMe\"\n class=\"w-4 h-4 text-blue-500 bg-gray-100 border-gray-400 rounded dark:ring-offset-gray-800 dark:bg-gray-700 dark:border-gray-600\" />\n <label for=\"rememberMe\" class=\"block ml-3 text-sm text-gray-800\"\n >Remember me</label\n >\n </div>\n <input\n type=\"hidden\"\n name=\"credentialId\"\n :value=\"\n context.auth.selectedCredential ? context.auth.selectedCredential : ''\n \" />\n <button\n class=\"w-full mt-6 px-4 py-2 tracking-wide text-white transition-colors duration-200 transform bg-blue-500 rounded-md hover:bg-blue-400 focus:outline-none focus:bg-blue-400\">\n Login\n </button>\n </form>\n </layout>\n</template>\n<script lang=\"ts\">\nimport { defineComponent } from \"vue\";\nimport Layout from \"~/components/Layout.vue\";\nimport ErrorBox from \"~/components/ErrorBox.vue\";\nimport type { KcContextBase } from \"~/types/context\";\n\nexport default defineComponent({\n name: \"Login\",\n components: {\n Layout,\n ErrorBox,\n },\n data() {\n return {\n context: (window as any).kcContext as KcContextBase.Login,\n };\n },\n});\n</script>\n<style>\n@tailwind base;\n@tailwind components;\n@tailwind utilities;\n</style>\n","/home/oliver/git-repos/keycloak-theme-vuejs/src/views/webauthn-authenticate/index.ts",[],"/home/oliver/git-repos/keycloak-theme-vuejs/src/views/webauthn-authenticate/index.vue",["53","54","55","56","57","58","59","60","61","62","63","64"],"<template>\n <layout>\n <h1\n v-if=\"context.isUserIdentified\"\n class=\"text-3xl font-semibold text-center text-gray-700\">\n Welcome {{ context.auth?.attemptedUsername }}!\n </h1>\n <h1 v-else class=\"text-3xl font-semibold text-center text-gray-700\">\n Welcome!\n </h1>\n <div v-if=\"webauthnSupported && !error\">\n <p class=\"mt-5 mb-5 text-center\">\n Please use one of your registered devices to continue:\n </p>\n <div\n v-for=\"authenticator in context.authenticators.authenticators\"\n :key=\"authenticator.credentialId\"\n class=\"w-full h-20 mb-5 bg-white shadow-md rounded-md flex flex-row items-center\">\n <svg\n class=\"w-12 h-12 ml-4 text-gray-700\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\"\n height=\"16\"\n fill=\"currentColor\"\n viewBox=\"0 0 16 16\">\n <path\n d=\"M6 .5a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 .5.5v4H6v-4ZM7 1v1h1V1H7Zm2 0v1h1V1H9ZM5.5 5a.5.5 0 0 0-.5.5V15a1 1 0 0 0 1 1h5a1 1 0 0 0 1-1V5.5a.5.5 0 0 0-.5-.5h-6Z\" />\n </svg>\n <div class=\"w-full ml-5 flex flex-col\">\n <p>{{ authenticator.label }}</p>\n <p class=\"text-sm\">\n Registered: {{ new Date(authenticator.createdAt).toLocaleString() }}\n </p>\n </div>\n </div>\n <button\n :onclick=\"prepareAuthenticate\"\n class=\"w-full px-4 py-2 tracking-wide text-white transition-colors duration-200 transform bg-blue-500 rounded-md hover:bg-blue-400 focus:outline-none focus:bg-blue-400\">\n Authenticate\n </button>\n <p\n class=\"mt-5 text-sm text-center\"\n v-if=\"context.auth?.showTryAnotherWayLink\">\n Don't have your device at hand?\n <a\n :onclick=\"tryAnotherWay\"\n class=\"text-blue-500 hover:underline cursor-pointer\"\n >Try another way.</a\n >\n </p>\n </div>\n <div v-if=\"!webauthnSupported\">\n <p class=\"mt-5 text-center\">\n It seems that your browser doesn't support WebAuthn. Please try logging\n in with a different browser<span\n v-if=\"!context.auth?.showTryAnotherWayLink\"\n >.</span\n ><span v-else\n >, or try a different login method using the button below.</span\n >\n </p>\n <button\n v-if=\"context.auth?.showTryAnotherWayLink\"\n :onclick=\"tryAnotherWay\"\n class=\"w-full mt-5 px-4 py-2 tracking-wide text-white transition-colors duration-200 transform bg-blue-500 rounded-md hover:bg-blue-400 focus:outline-none focus:bg-blue-400\">\n Try another method\n </button>\n </div>\n <div v-if=\"error\" class=\"mt-5 flex flex-col\">\n <ErrorBox\n >Something went wrong during authentication using your device.</ErrorBox\n >\n <button\n :onclick=\"retryAuth\"\n class=\"w-full mt-5 px-4 py-2 tracking-wide text-white transition-colors duration-200 transform bg-blue-500 rounded-md hover:bg-blue-400 focus:outline-none focus:bg-blue-400\">\n Retry\n </button>\n </div>\n </layout>\n</template>\n<script lang=\"ts\">\nimport { defineComponent } from \"vue\";\nimport Layout from \"~/components/Layout.vue\";\nimport ErrorBox from \"~/components/ErrorBox.vue\";\nimport type { KcContextBase } from \"~/types/context\";\nimport { base64url } from \"rfc4648\";\n\nexport default defineComponent({\n name: \"Login\",\n components: {\n Layout,\n ErrorBox,\n },\n data() {\n return {\n context: (window as any).kcContext as KcContextBase.WebauthnAuthenticate,\n webauthnSupported: true,\n error: false,\n };\n },\n mounted: function () {\n if (typeof PublicKeyCredential === \"undefined\") {\n this.webauthnSupported = false;\n }\n },\n methods: {\n tryAnotherWay(e: Event) {\n e.preventDefault();\n this.formPost(this.context.url.loginAction, {\n tryAnotherWay: \"on\",\n });\n },\n prepareAuthenticate() {\n if (this.context.isUserIdentified) {\n const allowedCredentials = new Array<PublicKeyCredentialDescriptor>();\n this.context.authenticators.authenticators.forEach(authenticator => {\n allowedCredentials.push({\n id: base64url.parse(authenticator.credentialId, { loose: true }),\n type: \"public-key\",\n });\n });\n this.authenticate(allowedCredentials);\n } else {\n this.authenticate([]);\n }\n },\n async authenticate(\n allowedAuthenticators: Array<PublicKeyCredentialDescriptor>\n ) {\n const publicKey: PublicKeyCredentialRequestOptions = {\n rpId: this.context.rpId,\n challenge: base64url.parse(this.context.challenge, { loose: true }),\n };\n if (this.context.createTimeout !== \"0\")\n publicKey.timeout = Number(this.context.createTimeout) * 1000;\n if (allowedAuthenticators.length)\n publicKey.allowCredentials = allowedAuthenticators;\n if (this.context.userVerification !== \"not specified\")\n publicKey.userVerification = this.context.userVerification;\n\n try {\n const resultRaw = await navigator.credentials.get({ publicKey });\n if (!resultRaw || resultRaw.type !== \"public-key\") return;\n const result = resultRaw as PublicKeyCredential;\n if (!(\"authenticatorData\" in result.response)) return;\n const response = result.response as AuthenticatorAssertionResponse;\n const clientDataJSON = response.clientDataJSON;\n const authenticatorData = response.authenticatorData;\n const signature = response.signature;\n\n const postData = {\n credentialId: result.id,\n clientDataJSON: base64url.stringify(new Uint8Array(clientDataJSON), {\n pad: false,\n }),\n authenticatorData: base64url.stringify(\n new Uint8Array(authenticatorData),\n { pad: false }\n ),\n signature: base64url.stringify(new Uint8Array(signature), {\n pad: false,\n }),\n userHandle: base64url.stringify(\n new Uint8Array(response.userHandle!),\n { pad: false }\n ),\n };\n\n this.formPost(this.context.url.loginAction, postData);\n } catch (err) {\n this.error = true;\n }\n },\n retryAuth() {\n this.error = false;\n },\n formPost(url: string, data: object) {\n const form = document.createElement(\"form\");\n form.method = \"post\";\n form.action = url;\n\n for (const key in data) {\n if (Object.prototype.hasOwnProperty.call(data, key)) {\n const hiddenField = document.createElement(\"input\");\n hiddenField.type = \"hidden\";\n hiddenField.name = key;\n hiddenField.value = data[key];\n\n form.appendChild(hiddenField);\n }\n }\n document.body.appendChild(form);\n form.submit();\n },\n },\n});\n</script>\n<style>\n@tailwind base;\n@tailwind components;\n@tailwind utilities;\n</style>\n","/home/oliver/git-repos/keycloak-theme-vuejs/tailwind.config.js",[],"/home/oliver/git-repos/keycloak-theme-vuejs/webpack.config.js",["65"],"const path = require(\"path\");\nconst { VueLoaderPlugin } = require(\"vue-loader\");\nconst HtmlWebpackPlugin = require(\"html-webpack-plugin\");\nconst { CleanWebpackPlugin } = require(\"clean-webpack-plugin\");\nconst CopyWebpackPlugin = require(\"copy-webpack-plugin\");\n\nconst THEME_NAME = \"modern-login\";\nconst customPages = [\"login\", \"webauthn-authenticate\"];\n\nmodule.exports = function (env, argv) {\n const isDevelopment = argv.mode !== \"production\";\n return {\n entry: () => {\n const entryList = {};\n for (const entry of customPages) {\n entryList[entry] = path.resolve(\n __dirname,\n \"src\",\n \"views\",\n entry,\n \"index.ts\"\n );\n }\n return entryList;\n },\n output: {\n path: path.resolve(__dirname, \"dist\", \"theme\", THEME_NAME, \"login\"),\n filename: \"resources/js/[name].js\",\n publicPath: \"/\",\n },\n devtool: \"inline-cheap-module-source-map\",\n resolve: {\n extensions: [\".ts\", \".tsx\", \".js\", \".vue\", \".json\", \".scss\"],\n alias: {\n \"~\": path.resolve(__dirname, \"src\"),\n },\n },\n mode: isDevelopment ? \"development\" : \"production\",\n watch: isDevelopment,\n module: {\n rules: [\n {\n test: /\\.vue$/,\n loader: \"vue-loader\",\n options: {\n sourceMap: isDevelopment,\n extract: false,\n },\n },\n {\n test: /\\.(ts|tsx)$/,\n exclude: /node_modules/,\n use: {\n loader: \"babel-loader\",\n options: {\n presets: [\"@babel/preset-env\", \"@babel/preset-typescript\"],\n plugins: [\n \"@babel/plugin-transform-runtime\",\n \"@babel/plugin-transform-typescript\",\n ],\n },\n },\n },\n {\n test: /\\.css$/,\n use: [\n \"style-loader\",\n \"css-loader\",\n {\n loader: \"postcss-loader\",\n options: {\n postcssOptions: {\n plugins: {\n tailwindcss: {},\n autoprefixer: {},\n },\n },\n },\n },\n ],\n },\n ],\n },\n plugins: [\n new CleanWebpackPlugin(),\n new VueLoaderPlugin(),\n ...customPages.map(\n entry =>\n new HtmlWebpackPlugin({\n inject: false,\n template: path.resolve(\n __dirname,\n \"src\",\n \"views\",\n entry,\n \"index.ftl\"\n ),\n filename: `${entry}.ftl`,\n minify: false,\n })\n ),\n new CopyWebpackPlugin({\n patterns: [\n {\n from: path.resolve(__dirname, \"src\", \"static\", \"login\"),\n to: path.resolve(__dirname, \"dist\", \"theme\", THEME_NAME, \"login\"),\n },\n {\n from: path.resolve(__dirname, \"src\", \"static\", \"jar\"),\n to: path.resolve(__dirname, \"dist\"),\n },\n ],\n }),\n ],\n ...(isDevelopment\n ? {}\n : {\n optimization: {\n removeAvailableModules: false,\n removeEmptyChunks: false,\n splitChunks: false,\n },\n }),\n };\n};\n",{"ruleId":"66","severity":2,"message":"67","line":4,"column":1,"nodeType":"68","messageId":"69","endLine":4,"endColumn":136},{"ruleId":"70","severity":2,"message":"71","line":18,"column":9,"nodeType":"72","messageId":"73","endLine":18,"endColumn":17},{"ruleId":"66","severity":2,"message":"74","line":36,"column":1,"nodeType":"68","messageId":"69","endLine":36,"endColumn":187},{"ruleId":"66","severity":2,"message":"75","line":47,"column":1,"nodeType":"68","messageId":"69","endLine":47,"endColumn":189},{"ruleId":"66","severity":2,"message":"76","line":62,"column":1,"nodeType":"68","messageId":"69","endLine":62,"endColumn":143},{"ruleId":"66","severity":2,"message":"77","line":74,"column":1,"nodeType":"68","messageId":"69","endLine":74,"endColumn":183},{"ruleId":"70","severity":2,"message":"78","line":87,"column":9,"nodeType":"72","messageId":"73","endLine":87,"endColumn":16},{"ruleId":"66","severity":2,"message":"79","line":27,"column":1,"nodeType":"68","messageId":"69","endLine":27,"endColumn":174},{"ruleId":"66","severity":2,"message":"80","line":38,"column":1,"nodeType":"68","messageId":"69","endLine":38,"endColumn":178},{"ruleId":"66","severity":2,"message":"77","line":65,"column":1,"nodeType":"68","messageId":"69","endLine":65,"endColumn":183},{"ruleId":"66","severity":2,"message":"77","line":75,"column":1,"nodeType":"68","messageId":"69","endLine":75,"endColumn":183},{"ruleId":"70","severity":2,"message":"78","line":89,"column":9,"nodeType":"72","messageId":"73","endLine":89,"endColumn":16},{"ruleId":"81","severity":2,"message":"82","line":115,"column":36,"nodeType":"83","messageId":"84","endLine":115,"endColumn":78},{"ruleId":"85","severity":2,"message":"86","line":115,"column":46,"nodeType":"87","messageId":"88","endLine":115,"endColumn":75},{"ruleId":"89","severity":2,"message":"90","line":127,"column":5,"nodeType":"91","messageId":"92","endLine":173,"endColumn":6},{"ruleId":"85","severity":2,"message":"86","line":128,"column":36,"nodeType":"87","messageId":"88","endLine":128,"endColumn":65},{"ruleId":"85","severity":2,"message":"93","line":130,"column":24,"nodeType":"87","messageId":"88","endLine":130,"endColumn":57},{"ruleId":"85","severity":2,"message":"94","line":144,"column":37,"nodeType":"87","messageId":"88","endLine":144,"endColumn":56},{"ruleId":"85","severity":2,"message":"95","line":146,"column":45,"nodeType":"87","messageId":"88","endLine":146,"endColumn":75},{"ruleId":"89","severity":2,"message":"96","line":10,"column":18,"nodeType":"97","messageId":"92","endLine":125,"endColumn":2},"max-len","This line has a length of 135. Maximum allowed is 120.","Program","max","vue/multi-word-component-names","Component name \"Layout\" should always be multi-word.","Literal","unexpected","This line has a length of 186. Maximum allowed is 120.","This line has a length of 188. Maximum allowed is 120.","This line has a length of 142. Maximum allowed is 120.","This line has a length of 182. Maximum allowed is 120.","Component name \"Login\" should always be multi-word.","This line has a length of 173. Maximum allowed is 120.","This line has a length of 177. Maximum allowed is 120.","no-array-constructor","The array literal notation [] is preferable.","NewExpression","preferLiteral","no-undef","'PublicKeyCredentialDescriptor' is not defined.","Identifier","undef","max-lines-per-function","Async method 'authenticate' has too many lines (44). Maximum allowed is 30.","Property","exceed","'PublicKeyCredentialRequestOptions' is not defined.","'PublicKeyCredential' is not defined.","'AuthenticatorAssertionResponse' is not defined.","Function has too many lines (116). Maximum allowed is 30.","FunctionExpression"]