{"id":15022300,"url":"https://github.com/starbasehq/sveltekit-openid-connect","last_synced_at":"2025-04-12T12:53:18.335Z","repository":{"id":38449429,"uuid":"357336644","full_name":"starbasehq/sveltekit-openid-connect","owner":"starbasehq","description":null,"archived":false,"fork":false,"pushed_at":"2024-04-10T20:26:22.000Z","size":619,"stargazers_count":16,"open_issues_count":9,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-26T07:36:38.815Z","etag":null,"topics":["auth0","authentication","cookies"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/starbasehq.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-04-12T20:55:01.000Z","updated_at":"2024-05-13T18:32:42.000Z","dependencies_parsed_at":"2024-04-02T21:29:31.450Z","dependency_job_id":"b3a22f0d-bcef-43f3-997c-00283b2cb6f8","html_url":"https://github.com/starbasehq/sveltekit-openid-connect","commit_stats":{"total_commits":44,"total_committers":1,"mean_commits":44.0,"dds":0.0,"last_synced_commit":"52316bf9c928b249e6a196b4e27f27e4f4981a0f"},"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/starbasehq%2Fsveltekit-openid-connect","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/starbasehq%2Fsveltekit-openid-connect/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/starbasehq%2Fsveltekit-openid-connect/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/starbasehq%2Fsveltekit-openid-connect/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/starbasehq","download_url":"https://codeload.github.com/starbasehq/sveltekit-openid-connect/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248111880,"owners_count":21049577,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["auth0","authentication","cookies"],"created_at":"2024-09-24T19:57:45.919Z","updated_at":"2025-04-12T12:53:18.315Z","avatar_url":"https://github.com/starbasehq.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SvelteKit OpenID Connect\n\nThis is an attempt to port [express-openid-connect](https://github.com/auth0/express-openid-connect) for use with SvelteKit\n\n[![NPM version](https://img.shields.io/npm/v/sveltekit-openid-connect.svg?style=flat-square)](https://npmjs.org/package/sveltekit-openid-connect)\n\n### Open issues for questions or concerns\n\n## Table of Contents\n\n- [Documentation](#documentation)\n- [Install](#install)\n- [Getting Started](#getting-started)\n- [Contributing](#contributing)\n- [Support + Feedback](#support--feedback)\n- [Vulnerability Reporting](#vulnerability-reporting)\n- [What is Auth0](#what-is-auth0)\n- [License](#license)\n\n## Install (Pending)\n\nNode.js version **\u003e=16.0.0** is recommended\n\n```bash\nnpm install sveltekit-openid-connect\n```\n\n## Getting Started\n\n### Initializing\n\n\u003e svelte.config.js\n```js\nconst config = {\n\tkit: {\n\t\tcsrf: { // This is required due to a breaking change in sveltekit see https://github.com/starbasehq/sveltekit-openid-connect/issues/11\n\t\t\tcheckOrigin: false\n\t\t}\n\t}\n}\n```\n\n\u003e src/hooks.server.js\n\n```js\nimport * as cookie from 'cookie'\nimport { TokenUtils } from 'sveltekit-openid-connect'\nimport { SessionService } from '$lib/services' // This is a service that provides session storage, not a part of this package\nimport fetch from 'node-fetch'\n\nconst {\n    AUTH0_DOMAIN,\n    AUTH0_BASE_URL,\n    AUTH0_CLIENT_ID,\n    AUTH0_CLIENT_SECRET,\n    COOKIE_SECRET,\n    AUTH0_AUDIENCE,\n\tCSRF_ALLOWED\n} = process.env\n\nconst csrfAllowed = [`https://${AUTH0_DOMAIN}`, ...(CSRF_ALLOWED || '').split(',').filter(Boolean)]\n\nconst sessionName = 'sessionName'\nconst auth0config = {\n    attemptSilentLogin: true,\n    authRequired: false,\n    auth0Logout: true, // Boolean value to enable Auth0's logout feature.\n    baseURL: AUTH0_BASE_URL,\n    clientID: AUTH0_CLIENT_ID,\n    issuerBaseURL: `https://${AUTH0_DOMAIN}`,\n    secret: COOKIE_SECRET,\n    clientSecret: AUTH0_CLIENT_SECRET,\n    authorizationParams: {\n        scope: 'openid profile offline_access email',\n        response_type: 'code id_token',\n        audience: AUTH0_AUDIENCE\n    },\n    session: {\n        name: 'sessionName', // Replace with custom session name\n        cookie: {\n            path: '/'\n        },\n        absoluteDuration: 86400,\n        rolling: false,\n        rollingDuration: false\n    }\n}\n\n// This was added to support decrypting the encrypted session cookies we utilized\nconst tokenUtils = new TokenUtils(auth0config)\n\nexport async function handle ({ event, resolve }) {\n\tconst { forbidden: forbidCSRF, response: responseCSRF } = checkCSRF(event.request)\n\tif (forbidCSRF) return responseCSRF\n    try {\n        const request = event.request\n        event.locals.isAuthenticated = false\n        const cookies = cookie.parse(request.headers.get('cookie') || '')\n        const { url, body, params } = request\n        const path = url.pathname\n        const query = url.searchParams\n        let sessionCookie\n\n\t\tlet sessionValid = false\n\t\tlet session = {}\n\t\tif (cookies.session_id) event.locals.sessionId = cookies.session_id\n        try {\n\t\t\tif (event.cookies.get('session_id') \u0026\u0026 event.cookies.get(sessionName)) {\n\t\t\t\tconst cookieToken = event.cookies.get(sessionName)\n\t\t\t\tsession = await sessionService.get(cookies.session_id, cookieToken)\n\t\t\t\ttry {\n\t\t\t\t\tif (tokenUtils.isExpired(cookieToken)) {\n\t\t\t\t\t\tconsole.warn('Token is expired, try to renew')\n\t\t\t\t\t\t// TODO: Needs Testing\n\t\t\t\t\t\t// TODO: Support refresh tokens?\n\t\t\t\t\t\treturn Response.redirect(`/auth/login?returnTo=${event.url.pathname}`, 401)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst idToken = tokenUtils.getIdToken({ token: cookieToken })\n\t\t\t\t\t\tconst sToken = session.data\n\t\t\t\t\t\tif (sToken.exp \u003c idToken.exp) {\n\t\t\t\t\t\t\tconsole.debug('Cookie is Newer, update session')\n\t\t\t\t\t\t\t// Update session from your session service\n\t\t\t\t\t\t\tawait session.save()\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (sToken.sub === idToken.sub \u0026\u0026 sToken.iss === idToken.iss) {\n\t\t\t\t\t\t\tconsole.info('Cookie and Session match')\n\t\t\t\t\t\t\tsessionValid = true\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tconsole.error('Cookie and Session failed to match, do something')\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconsole.info('Token is valid, not expired')\n\t\t\t\t\t}\n\t\t\t\t} catch (tErr) {\n\t\t\t\t\tconsole.trace(tErr)\n\t\t\t\t}\n\n\t\t\t\tevent.locals.sessionId = cookies.session_id\n\n\t\t\t\tif (session) {\n                    // assign session information to event.locals here\n                    /*\n                        event.locals.user = session.data.user\n                    */\n                }\n            } else {\n                console.warn('No session found, better send to auth')\n\t\t\t\t// perform sveltekit redirect\n            }\n        } catch (err) {\n            console.error('problem getting app session', err.message)\n            // perform sveltekit redirect\n        }\n\n        const response = await resolve(request) // This is required by sveltekit\n\n        // Optional: add the session cookie\n        if (sessionCookie) {\n            const existingCookies = (response.headers \u0026\u0026 response.headers.get('set-cookie')) ? response.headers.get('set-cookie') : []\n            response.headers.set('set-cookie', [...existingCookies, sessionCookie])\n        }\n\n        return response\n    } catch (err) {\n        console.error('Problem running handle', err.message)\n    }\n}\n\nexport async function getSession (event) {\n    // This has been deprecated in sveltekit, it is safe to delete, it is moved to +layout.server.js\n}\n\n// This is required due to sveltekit changes see https://github.com/starbasehq/sveltekit-openid-connect/issues/11\nfunction checkCSRF (request) {\n\tconst url = new URL(request.url)\n\tconst type = request.headers.get('content-type')?.split(';')[0]\n\tconst forbidden =\n\t\trequest.method === 'POST' \u0026\u0026\n\t\t!_.includes([url.origin, ...csrfAllowed], request.headers.get('origin')) \u0026\u0026\n\t\t(type === 'application/x-www-form-urlencoded' || type === 'multipart/form-data')\n\n\tif (forbidden) {\n\t\tconsole.warn('Prevent CSRF')\n\t\tconst response = new Response(`Cross-site ${request.method} form submissions are forbidden`, {\n\t\t\tstatus: 403\n\t\t})\n\t\treturn { forbidden, response }\n\t} else {\n\t\treturn { forbidden, response: null }\n\t}\n}\n```\n\n\u003e src/routes/+layout.server.js\n\n```js\nexport async function load ({ locals }) {\n\treturn {\n\t\tsession: {\n\t\t\tisAuthenticated: locals.isAuthenticated,\n\t\t\tsessionId: locals.sessionId,\n\t\t\tuser: locals.user\n\t\t}\n\t}\n}\n```\n\n### Logging in\n\nThe endpoint route can be different but must be changed in the config block for routes\n\u003e src/routes/auth/login/+server.js\n\n```js\nimport { Auth } from 'sveltekit-openid-connect'\n\nconst {\n    AUTH0_DOMAIN,\n    AUTH0_BASE_URL,\n    AUTH0_CLIENT_ID,\n    AUTH0_CLIENT_SECRET,\n    COOKIE_SECRET,\n    AUTH0_AUDIENCE\n} = process.env\n\nconst auth0config = {\n    attemptSilentLogin: true,\n    authRequired: false,\n    auth0Logout: true, // Boolean value to enable Auth0's logout feature.\n    baseURL: AUTH0_BASE_URL,\n    clientID: AUTH0_CLIENT_ID,\n    issuerBaseURL: `https://${AUTH0_DOMAIN}`,\n    secret: COOKIE_SECRET,\n    clientSecret: AUTH0_CLIENT_SECRET,\n    authorizationParams: {\n        scope: 'openid profile offline_access email groups permissions roles',\n        response_type: 'code id_token',\n        audience: AUTH0_AUDIENCE\n    },\n    routes: {\n        login: '/auth/login',\n        logout: '/auth/logout',\n        callback: '/auth/callback'\n    }\n}\n\nconst auth0 = new Auth(auth0config)\n\nexport async function get ({ request }, ...otherProps) {\n    const loginResponse = await auth0.handleLogin()\n\n\treturn new Response(JSON.stringify({}), {\n\t\tstatus: 302,\n\t\theaders: {\n\t\t\tlocation: loginResponse.authorizationUrl,\n\t\t\t'Set-Cookie': loginResponse.cookies\n\t\t}\n\t})\n}\n```\n\n### Handling the callback\n\nThe endpoint route can be different but must be changed in the config block for routes\n\u003e src/routes/auth/callback/+server.js\n\n```js\nimport _ from 'lodash'\nimport * as cookie from 'cookie'\nimport { Auth, appSession } from 'sveltekit-openid-connect'\nimport mock from 'mock-http'\nimport { SessionService } from '$lib/services'\n\nconst sessionService = new SessionService()\n\nconst {\n    AUTH0_DOMAIN,\n    AUTH0_BASE_URL,\n    AUTH0_CLIENT_ID,\n    AUTH0_CLIENT_SECRET,\n    COOKIE_SECRET,\n    AUTH0_AUDIENCE\n} = process.env\n\nconst auth0config = {\n    attemptSilentLogin: true,\n    authRequired: false,\n    auth0Logout: true, // Boolean value to enable Auth0's logout feature.\n    baseURL: AUTH0_BASE_URL,\n    clientID: AUTH0_CLIENT_ID,\n    issuerBaseURL: `https://${AUTH0_DOMAIN}`,\n    secret: COOKIE_SECRET,\n    clientSecret: AUTH0_CLIENT_SECRET,\n    authorizationParams: {\n        scope: 'openid profile offline_access email',\n        response_type: 'code id_token',\n        audience: AUTH0_AUDIENCE\n    },\n    session: {\n        name: 'sessionName',\n        cookie: {\n            path: '/'\n        },\n        absoluteDuration: 86400,\n        rolling: false,\n        rollingDuration: false\n    }\n}\n\nconst auth0 = new Auth(auth0config)\n\nexport async function post ({ request }) {\n    const { headers } = request\n    const body = await request.formData()\n    const cookies = cookie.parse(headers.get('cookie') || '')\n    if (_.isObject(cookies)) {\n        const req = new mock.Request({\n            url: request.url,\n            method: 'POST',\n            headers,\n            buffer: Buffer.from(JSON.stringify({\n                code: body.get('code'),\n                state: body.get('state'),\n                id_token: body.get('id_token')\n            }))\n        })\n        req.cookies = cookies\n        req.body = {\n            code: body.get('code'),\n            state: body.get('state'),\n            id_token: body.get('id_token')\n        }\n\n        const res = new mock.Response()\n\n        const authResponse = await auth0.handleCallback(req, res, cookies)\n        const session = await appSession(auth0config)(req, res, authResponse.session)\n\n\t\t// Optional to allow restoring an existing session\n\t\tconst rReturn = new URL(authResponse.redirect.returnTo)\n\t\tlet sessionId = cookies['session_id'] || rReturn.searchParams.get('sid')\n\t\tlet sessionRestored = false\n\t\tlet sessionCookie\n\n\t\tif (sessionId) {\n\t\t\tconst restoredSession = await sessionService.restoreSession(sessionId, authResponse.session)\n\t\t\tif (restoredSession.ok) {\n\t\t\t\tsessionRestored = true\n\t\t\t}\n\t\t}\n\n\t\tif (!sessionRestored) {\n\t\t\tconst newSession = await sessionService.createSession(authResponse.session)\n\t\t\tsessionId = newSession.sessionId\n\t\t\tsessionCookie = cookie.serialize('session_id', sessionId, {\n\t\t\t\thttpOnly: true,\n\t\t\t\tmaxAge: 60 * 60 * 24 * 30,\n\t\t\t\tsameSite: 'lax',\n\t\t\t\tpath: '/'\n\t\t\t})\n\t\t}\n\n        return new Response(JSON.stringify({\n\t\t\terror: false\n\t\t}), {\n\t\t\tstatus: 302,\n\t\t\theaders: {\n\t\t\t\tlocation: '/',\n\t\t\t\t'set-cookie': _.concat(authResponse.cookies, session.cookies, sessionCookie).filter(Boolean)\n\t\t\t}\n\t\t})\n    } else {\n\t\treturn new Response(JSON.stringify({\n\t\t\terror: true\n\t\t}))\n\t}\n}\n\n```\n\n### Destroying the Session\n\n\u003e src/routes/auth/logout/+server.js\n\n```js\nimport * as cookie from 'cookie'\nimport { Auth } from 'sveltekit-openid-connect'\nimport mock from 'mock-http'\n\nconst {\n    AUTH0_DOMAIN,\n    AUTH0_BASE_URL,\n    AUTH0_CLIENT_ID,\n    AUTH0_CLIENT_SECRET,\n    COOKIE_SECRET,\n    AUTH0_AUDIENCE\n} = process.env\n\nconst auth0config = {\n    attemptSilentLogin: true,\n    authRequired: false, // Require authentication for all routes.\n    auth0Logout: true, // Boolean value to enable Auth0's logout feature.\n    baseURL: AUTH0_BASE_URL,\n    clientID: AUTH0_CLIENT_ID,\n    issuerBaseURL: `https://${AUTH0_DOMAIN}`,\n    secret: COOKIE_SECRET,\n    clientSecret: AUTH0_CLIENT_SECRET,\n    authorizationParams: {\n        scope: 'openid profile offline_access email groups permissions roles',\n        response_type: 'code id_token',\n        audience: AUTH0_AUDIENCE\n    },\n    session: {\n        name: 'sessionName',\n        cookie: {\n            path: '/'\n        },\n        absoluteDuration: 86400,\n        rolling: false,\n        rollingDuration: false\n    },\n    routes: {\n        login: '/auth/login',\n        logout: '/auth/logout',\n        callback: '/auth/callback'\n    }\n}\n\nconst auth0 = new Auth(auth0config)\n\nexport async function get ({ locals, request }) {\n    const { headers } = request\n    const cookies = cookie.parse(headers.get('cookie') || '')\n\n    const res = new mock.Response()\n    const logoutResponse = await auth0.handleLogout(request, res, cookies, Object.assign(locals))\n\n\t// Optional, remove this if you want to support restoring previous session\n    const sessionCookie = cookie.serialize('session_id', 'deleted', {\n        httpOnly: true,\n        expires: new Date(),\n        sameSite: 'lax',\n        path: '/'\n    })\n\n    return new Response(JSON.stringify({}), {\n\t\tstatus: 302,\n\t\theaders: {\n\t\t\tlocation: logoutResponse.returnURL,\n\t\t\t'Set-Cookie': [...logoutResponse.cookies]\n\t\t}\n\t})\n}\n```\n\n### Sample Session Service\n\n\u003e src/lib/services/session.js\n\n```js\nimport { jwtDecode } from 'jwt-decode'\nimport { v4 as uuidv4 } from 'uuid'\nimport DB from './db' // Custom database service using sequelize\nimport UserService from './user'\n\nconst db = new DB()\nconst userService = new UserService()\n\nclass SessionService {\n    async createSession (authSession) {\n        const sqldb = await db.getDatabase()\n        const sessionId = uuidv4()\n        const session = await jwtDecode(authSession.id_token)\n        console.log('Create Session', session)\n\n        // enrich with raw oidc session data\n        session.oidc = authSession\n\n        const { email, sub } = session\n        const [identitySource, userIdentifier] = sub.split('|')\n        const userData = {\n            identitySource,\n            userIdentifier,\n            email,\n            user_id: sub\n        }\n\n        const userProfile = await userService.get(userData)\n\n        const { UserId, ...other } = userProfile\n\n        session.UserId = UserId\n        session.user = {}\n        session.user.other = other\n\n        const [sessionStore, created] = await sqldb.SessionStore.findOrCreate({\n            where: {\n                sessionId\n            },\n            defaults: {\n                data: session,\n                sessionId,\n                UserId\n            }\n        })\n\n        if (created) {\n            console.log('Created SessionStore', sessionStore._id)\n        }\n\n        return { sessionId, session: sessionStore }\n    }\n\n    async get (sessionId) {\n        const session = await getSession(sessionId)\n\n        return session\n    }\n\n    async decodeJwt (jwt) {\n        return jwtDecode(jwt)\n    }\n\n\tasync restoreSession (sessionId, authSession) {\n\t\tconst rSession = await getSession(sessionId)\n\n\t\tif (!rSession) return { ok: false }\n\n\t\tconst session = await jwtDecode(authSession.id_token)\n\n\t\t// enrich with raw oidc session data\n\t\tsession.oidc = authSession\n\n\t\tconst { email, sub } = session\n\t\tconst [identitySource, userIdentifier] = sub.split('|')\n\t\tconst userData = {\n\t\t\tidentitySource,\n\t\t\tuserIdentifier,\n\t\t\temail,\n\t\t\tuser_id: sub\n\t\t}\n\n\t\tif (session.sub !== rSession.data.sub) {\n\t\t\tconsole.info(`Session is not for authed User ${session.sub} vs ${rSession.data.sub}`)\n\t\t\treturn { ok: false }\n\t\t}\n\n\t\tconst userProfile = await userService.get(userData)\n\n\t\tconst { orgs, projects } = userProfile\n\t\tsession.user = {}\n\t\tsession.user.orgs = orgs || []\n\t\tsession.user.projects = projects || []\n\n\t\ttry {\n\t\t\trSession.data = Object.assign(rSession.data, session)\n\t\t\trSession.changed('data', true)\n\n\t\t\tawait rSession.save()\n\t\t\treturn { ok: true }\n\t\t} catch (e) {\n\t\t\tconsole.error(e.message)\n\t\t\treturn { ok: false }\n\t\t}\n\t}\n}\n\nasync function getSession (sessionId) {\n    const sqldb = await db.getDatabase()\n    const session = await sqldb.SessionStore.findOne({\n        where: {\n            sessionId\n        }\n    })\n\n    return session\n}\n\nexport default SessionService\n```\n\n\u003e src/lib/services/db.js\n\n```js\nimport _ from 'lodash'\nimport orm from '\u003c\u003csequelize orm project\u003e\u003e'\n\nconst config = {\n    sequelize: {\n        // eslint-disable-next-line dot-notation\n        sync: process.env['DB_SYNC'],\n        // eslint-disable-next-line dot-notation\n        syncForce: process.env['DB_SYNC_FORCE'],\n        // eslint-disable-next-line dot-notation\n        database: process.env['DB_DATABASE'] ,\n        // eslint-disable-next-line dot-notation\n        host: process.env['DB_HOST'] || '127.0.0.1',\n        // eslint-disable-next-line dot-notation\n        port: process.env['DB_PORT'],\n        // eslint-disable-next-line dot-notation\n        username: process.env['DB_USERNAME'],\n        // eslint-disable-next-line dot-notation\n        password: process.env['DB_PASSWORD'],\n        // eslint-disable-next-line dot-notation\n        dbDefault: process.env['DB_DEFAULT'] || 'postgres',\n        dialectOptions: {\n            // eslint-disable-next-line dot-notation\n            ssl: process.env['DB_SSL'] === 'true'\n        }\n    }\n}\n\nconst sqldb = orm(config)\nclass DatabaseService {\n    async getDatabase () {\n        return sqldb\n    }\n}\nexport default DatabaseService\n```\n\n\u003e src/lib/services/user.js\n\n```js\nimport _ from 'lodash'\nimport DB from './db'\n\nconst db = new DB()\n\nclass UserService {\n    async get (userData) {\n        const sqldb = await db.getDatabase()\n\n        const [user, created] = await sqldb.User.findOrCreate({\n            where: {\n                email: userData.email\n            },\n            defaults: userData\n        })\n\n        if (!created) {\n            if (!user.userIdentifier) {\n                await updateUserAttributes(user, userData)\n            }\n            console.debug('Existing User')\n            return {\n                UserId: user._id\n            }\n        } else {\n            console.debug('created new user', JSON.stringify(user, null, 2))\n            return {\n                UserId: user._id\n            }\n        }\n    }\n}\n\nasync function updateUserAttributes (user, aUser) {\n    const identitySource = (aUser.identitySource) ? aUser.identitySource : aUser.identities[0].provider // TODO: should we always assume 0?\n    const userIdentifier = (aUser.userIdentifier) ? aUser.userIdentifier : aUser.identities[0].user_id // TODO: should we always assume 0?\n\n    user.identitySource = identitySource\n    user.userIdentifier = userIdentifier\n    user.user_id = aUser.user_id\n    await user.save()\n}\n\nexport default UserService\n```\n\n\n## Contributing\n\nWe appreciate feedback and contribution to this repo! Before you get started, please see the following:\n\nContributions can be made to this library through PRs to fix issues, improve documentation or add features. Please fork this repo, create a well-named branch, and submit a PR with a complete template filled out.\n\nCode changes in PRs should be accompanied by tests covering the changed or added functionality. Tests can be run for this library with:\n\n```bash\nnpm install\nnpm test\n```\n\nWhen you're ready to push your changes, please run the lint command first:\n\n```bash\nnpm run lint\n```\n\n## Support + Feedback\n\nPlease use the [Issues queue](https://github.com/starbasehq/sveltekit-openid-connect/issues) in this repo for questions and feedback.\n\n## What is Auth0?\n\nAuth0 helps you to easily:\n\n- implement authentication with multiple identity providers, including social (e.g., Google, Facebook, Microsoft, LinkedIn, GitHub, Twitter, etc), or enterprise (e.g., Windows Azure AD, Google Apps, Active Directory, ADFS, SAML, etc.)\n- log in users with username/password databases, passwordless, or multi-factor authentication\n- link multiple user accounts together\n- generate signed JSON Web Tokens to authorize your API calls and flow the user identity securely\n- access demographics and analytics detailing how, when, and where users are logging in\n- enrich user profiles from other data sources using customizable JavaScript rules\n\n[Why Auth0?](https://auth0.com/why-auth0)\n\n## License\n\nThis project is licensed under the MIT license. See the [LICENSE](LICENSE) file for more info.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstarbasehq%2Fsveltekit-openid-connect","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstarbasehq%2Fsveltekit-openid-connect","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstarbasehq%2Fsveltekit-openid-connect/lists"}