{
  "version": 3,
  "sources": ["../src/oauth.ts"],
  "sourcesContent": ["import fs from 'fs';\nimport path from 'path';\nimport express, { Router } from 'express';\nimport grant, { type GrantProvider, type GrantConfig, type GrantSession } from 'grant';\nimport session from 'express-session';\nimport { matchMaker } from '@colyseus/core';\nimport { type MayHaveUpgradeToken, auth } from './auth.ts';\n\n// @ts-ignore\nimport RedisStore from \"connect-redis\";\nimport { JWT } from './JWT.ts';\n\nexport type OAuthProviderName = '23andme' | '500px' | 'acton' | 'acuityscheduling' | 'adobe' | 'aha' | 'alchemer' | 'amazon' | 'angellist' | 'apple' | 'arcgis' | 'asana' | 'assembla' | 'atlassian' | 'auth0' | 'authentiq' | 'authing' | 'autodesk' | 'aweber' | 'axosoft' | 'baidu' | 'basecamp' | 'battlenet' | 'beatport' | 'bitbucket' | 'bitly' | 'box' | 'buffer' | 'campaignmonitor' | 'cas' | 'cheddar' | 'clio' | 'cognito' | 'coinbase' | 'concur' | 'constantcontact' | 'coursera' | 'crossid' | 'dailymotion' | 'deezer' | 'delivery' | 'deputy' | 'deviantart' | 'digitalocean' | 'discogs' | 'discord' | 'disqus' | 'docusign' | 'dribbble' | 'dropbox' | 'ebay' | 'echosign' | 'ecwid' | 'edmodo' | 'egnyte' | 'etsy' | 'eventbrite' | 'evernote' | 'eyeem' | 'facebook' | 'familysearch' | 'feedly' | 'figma' | 'fitbit' | 'flickr' | 'formstack' | 'foursquare' | 'freeagent' | 'freelancer' | 'freshbooks' | 'fusionauth' | 'garmin' | 'geeklist' | 'genius' | 'getbase' | 'getpocket' | 'gitbook' | 'github' | 'gitlab' | 'gitter' | 'goodreads' | 'google' | 'groove' | 'gumroad' | 'harvest' | 'hellosign' | 'heroku' | 'homeaway' | 'hootsuite' | 'huddle' | 'ibm' | 'iconfinder' | 'idme' | 'idonethis' | 'imgur' | 'infusionsoft' | 'instagram' | 'intuit' | 'jamendo' | 'jumplead' | 'kakao' | 'keycloak' | 'line' | 'linkedin' | 'live' | 'livechat' | 'logingov' | 'lyft' | 'mailchimp' | 'mailup' | 'mailxpert' | 'mapmyfitness' | 'mastodon' | 'medium' | 'meetup' | 'mendeley' | 'mention' | 'microsoft' | 'mixcloud' | 'moxtra' | 'myob' | 'naver' | 'nest' | 'netlify' | 'nokotime' | 'notion' | 'nylas' | 'okta' | 'onelogin' | 'openstreetmap' | 'optimizely' | 'osu' | 'patreon' | 'paypal' | 'phantauth' | 'pinterest' | 'plurk' | 'podio' | 'procore' | 'producthunt' | 'projectplace' | 'pushbullet' | 'qq' | 'ravelry' | 'redbooth' | 'reddit' | 'runkeeper' | 'salesforce' | 'sellsy' | 'shoeboxed' | 'shopify' | 'skyrock' | 'slack' | 'slice' | 'smartsheet' | 'smugmug' | 'snapchat' | 'snowflake' | 'socialpilot' | 'socrata' | 'soundcloud' | 'spotify' | 'square' | 'stackexchange' | 'stocktwits' | 'stormz' | 'storyblok' | 'strava' | 'stripe' | 'surveymonkey' | 'surveysparrow' | 'thingiverse' | 'ticketbud' | 'tiktok' | 'timelyapp' | 'todoist' | 'trakt' | 'traxo' | 'trello' | 'tripit' | 'trustpilot' | 'tumblr' | 'twitch' | 'twitter' | 'typeform' | 'uber' | 'unbounce' | 'underarmour' | 'unsplash' | 'untappd' | 'upwork' | 'uservoice' | 'vend' | 'venmo' | 'vercel' | 'verticalresponse' | 'viadeo' | 'vimeo' | 'visualstudio' | 'vk' | 'wechat' | 'weekdone' | 'weibo' | 'withings' | 'wordpress' | 'workos' | 'wrike' | 'xero' | 'xing' | 'yahoo' | 'yammer' | 'yandex' | 'zendesk' | 'zoom';\nexport type OAuthProviderConfig = {\n  /**\n   * consumer_key or client_id of your OAuth app\n   */\n  key: string;\n\n  /**\n   * consumer_secret or client_secret of your OAuth app\n   */\n  secret: string;\n\n  /**\n   * array of OAuth scopes to request\n   */\n  scope?: string[];\n\n  /**\n   * generate random nonce string (OpenID Connect only)\n   */\n  nonce?: boolean;\n\n  /**\n   * custom authorization parameters\n   */\n  custom_params?: any;\n\n  /**\n   * relative route or absolute URL to receive the response data /hello | https://site.com/hey\n   */\n  callback?: string;\n\n  /**\n   * relative route or absolute URL to receive the response data /hello | https://site.com/hey\n   */\n  response?: Array<'tokens' | 'raw' | 'jwt' | 'profile'>\n}\n\nexport type OAuthProviderCallback = (data: GrantSession['response'] & MayHaveUpgradeToken, provider: OAuthProviderName) => Promise<unknown>;\nexport let oAuthProviderCallback: (data: GrantSession['response'] & MayHaveUpgradeToken, provider: OAuthProviderName) => Promise<unknown> = async (data, provider) => {\n  console.debug(\"OAuth callback missing. Use oauth.onCallback() to persist user data.\");\n  return data;\n};\n\nexport const oauth = {\n  /**\n   * Default 'grant' module configuration.\n   */\n  defaults: {\n    transport: \"session\",\n    state: true,\n    response: [\"tokens\", \"raw\", \"profile\"],\n    // Allow 'origin' to be set dynamically per-request\n    // (needed when origin is auto-detected after grant middleware is initialized)\n    dynamic: ['origin'],\n  } as GrantProvider & { prefix: never },\n\n  /**\n   * Route prefix for OAuth routes.\n   */\n  prefix: \"/provider\",\n  providers: {} as { [providerId in OAuthProviderName]: OAuthProviderConfig },\n\n  /**\n   * Add OAuth provider configuration.\n   * @param providerId OAuth provider name\n   * @param config OAuth provider configuration\n   */\n  addProvider: function (providerId: OAuthProviderName, config: OAuthProviderConfig) {\n    this.providers[providerId] = config;\n  },\n\n  /**\n   * Provides a callback function that is called when OAuth is successful.\n   */\n  onCallback: function (callback: OAuthProviderCallback) {\n    oAuthProviderCallback = callback;\n  },\n\n  /**\n   * Returns an Express Router that handles OAuth for configured providers.\n   * @param callback (optional) Callback function that is called when OAuth is successful.\n   * @returns Express Router\n   */\n  routes: function (callback?: OAuthProviderCallback): Router {\n    if (callback) { this.onCallback(callback); }\n\n    const router = express.Router();\n\n    matchMaker.onReady.then(() => {\n      //\n      // Here we are using the same Redis connection from the MatchMaker.\n      // FIXME: make it type-safe. (This is a hacky way to auto-detection)\n      //\n      const store = (matchMaker.presence['pub'])\n        ? new RedisStore({ client: matchMaker.presence['pub'] })\n        : undefined;\n\n      const sessionMiddleware = session({\n        secret: process.env.SESSION_SECRET,\n        resave: false,\n        saveUninitialized: false, // true\n        store\n      });\n\n      // set prefix\n      const config: GrantConfig = Object.assign({ defaults: this.defaults }, this.providers);\n      config.defaults.prefix = oauth.prefix;\n\n      router.use(sessionMiddleware);\n\n      router.get(\"/:providerId\", async (req, res, next) => {\n        const providerId = req.params.providerId as OAuthProviderName;\n        if (oauth.providers[providerId]) {\n          next();\n\n        } else {\n          if (process.env.NODE_ENV === \"production\") {\n            //\n            // Production environment:\n            //\n            res.send(`Missing OAuth provider configuration for \"${providerId}\".`);\n\n          } else {\n            //\n            // Development environment:\n            // Display help URL for missing OAuth provider configuration\n            //\n            const helpURLs = JSON.parse(fs.readFileSync(path.normalize(__dirname + '/../oauth_help_urls.json')).toString());\n            const providerUrl = helpURLs[providerId];\n            res.send(`<!doctype html>\n<html>\n<head>\n<title>Missing \"${providerId}\" provider configuration</title>\n<style>p { font-family: -apple-system,BlinkMacSystemFont,\"Segoe UI\",\"Noto Sans\",Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"; }</style>\n</head>\n<body>\n<p>Missing config for \"${providerId}\" OAuth provider.</p>\n<hr />\n<p><small><strong>Config example:</strong></small></p>\n<pre><code>import { auth } from \"@colyseus/auth\";<br />\nauth.oauth.addProvider(\"${providerId}\", {\n  key: \"xxx\",\n  secret: \"xxx\",\n});\n</code></pre>\n${(providerUrl) ? `<hr/><p><small><em>(Get your keys from <a href=\"${providerUrl}\" target=\"_blank\">${providerUrl}</a>)</em></small></p>` : \"\"}\n</body>\n</html>`);\n          }\n        }\n      });\n\n      //\n      // Dynamically inject origin for grant per-request.\n      // This handles the case where origin is auto-detected after grant was initialized.\n      // Grant reads dynamic overrides from res.locals.grant.dynamic\n      //\n      router.use((req, res, next) => {\n        const dynamicOrigin = oauth.defaults.origin || auth.backend_url;\n        if (dynamicOrigin) {\n          res.locals.grant = {\n            dynamic: { origin: dynamicOrigin }\n          };\n        }\n        next();\n      });\n\n      router.use(grant.default.express(config));\n\n      router.get(\"/:providerId/callback\", async (req, res) => {\n        const session = (req as any).session as unknown & { grant: GrantSession };\n\n        let user = null;\n        let token = null;\n        let response = undefined;\n\n        if (session.grant.response.error) {\n          response = { error: session.grant.response.error, user, token, };\n\n        } else {\n          const data: GrantSession['response'] & MayHaveUpgradeToken = session.grant.response;\n\n          /**\n           * Verify existing token, if available\n           * (Upgrading user)\n           */\n          if (session.grant.dynamic?.token) {\n            data.upgradingToken = await JWT.verify(session.grant.dynamic?.token);\n          }\n\n          // transform profile data\n          if (data.profile) {\n            data.profile = oauth.transformProfileData(data.profile);\n          }\n\n          user = await oAuthProviderCallback(data, session.grant.provider as OAuthProviderName);\n          token = await auth.settings.onGenerateToken(user);\n          response = { user, token };\n        }\n\n        /**\n         * I believe it is safe to use \"*\" in the origin here, since the token and origin are already validated by the OAuth provider.\n         * => https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage\n         */\n        res.send(`<!DOCTYPE html><html><head><script type=\"text/javascript\">window.opener.postMessage(${JSON.stringify(response)}, '*');</script></head><body></body></html>`);\n        res.end();\n      });\n    });\n\n    return router;\n  },\n\n  /**\n   * Transform raw profile data into a single object.\n   * (e.g. Twitch returns an array of profiles, but we only need the first one)\n   * @param raw\n   */\n  transformProfileData(raw: any) {\n    if (raw.data && Array.isArray(raw.data) && raw.data.length === 1) {\n      //\n      // Twitch:\n      // Twitch returns an array of profiles, but we only need the first one\n      //\n      return raw.data[0];\n    } else {\n      //\n      // Fallback: return raw data\n      //\n      return raw;\n    }\n  }\n}"],
  "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gBAAe;AACf,kBAAiB;AACjB,qBAAgC;AAChC,mBAA+E;AAC/E,6BAAoB;AACpB,kBAA2B;AAC3B,kBAA+C;AAG/C,2BAAuB;AACvB,iBAAoB;AAyCb,IAAI,wBAAiI,OAAO,MAAM,aAAa;AACpK,UAAQ,MAAM,sEAAsE;AACpF,SAAO;AACT;AAEO,IAAM,QAAQ;AAAA;AAAA;AAAA;AAAA,EAInB,UAAU;AAAA,IACR,WAAW;AAAA,IACX,OAAO;AAAA,IACP,UAAU,CAAC,UAAU,OAAO,SAAS;AAAA;AAAA;AAAA,IAGrC,SAAS,CAAC,QAAQ;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKA,QAAQ;AAAA,EACR,WAAW,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOZ,aAAa,SAAU,YAA+B,QAA6B;AACjF,SAAK,UAAU,UAAU,IAAI;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,YAAY,SAAU,UAAiC;AACrD,4BAAwB;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAQ,SAAU,UAA0C;AAC1D,QAAI,UAAU;AAAE,WAAK,WAAW,QAAQ;AAAA,IAAG;AAE3C,UAAM,SAAS,eAAAA,QAAQ,OAAO;AAE9B,2BAAW,QAAQ,KAAK,MAAM;AAK5B,YAAM,QAAS,uBAAW,SAAS,KAAK,IACpC,IAAI,qBAAAC,QAAW,EAAE,QAAQ,uBAAW,SAAS,KAAK,EAAE,CAAC,IACrD;AAEJ,YAAM,wBAAoB,uBAAAC,SAAQ;AAAA,QAChC,QAAQ,QAAQ,IAAI;AAAA,QACpB,QAAQ;AAAA,QACR,mBAAmB;AAAA;AAAA,QACnB;AAAA,MACF,CAAC;AAGD,YAAM,SAAsB,OAAO,OAAO,EAAE,UAAU,KAAK,SAAS,GAAG,KAAK,SAAS;AACrF,aAAO,SAAS,SAAS,MAAM;AAE/B,aAAO,IAAI,iBAAiB;AAE5B,aAAO,IAAI,gBAAgB,OAAO,KAAK,KAAK,SAAS;AACnD,cAAM,aAAa,IAAI,OAAO;AAC9B,YAAI,MAAM,UAAU,UAAU,GAAG;AAC/B,eAAK;AAAA,QAEP,OAAO;AACL,cAAI,QAAQ,IAAI,aAAa,cAAc;AAIzC,gBAAI,KAAK,6CAA6C,UAAU,IAAI;AAAA,UAEtE,OAAO;AAKL,kBAAM,WAAW,KAAK,MAAM,UAAAC,QAAG,aAAa,YAAAC,QAAK,UAAU,YAAY,0BAA0B,CAAC,EAAE,SAAS,CAAC;AAC9G,kBAAM,cAAc,SAAS,UAAU;AACvC,gBAAI,KAAK;AAAA;AAAA;AAAA,kBAGH,UAAU;AAAA;AAAA;AAAA;AAAA,yBAIH,UAAU;AAAA;AAAA;AAAA;AAAA,0BAIT,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA,EAKjC,cAAe,mDAAmD,WAAW,qBAAqB,WAAW,2BAA2B,EAAE;AAAA;AAAA,QAErI;AAAA,UACE;AAAA,QACF;AAAA,MACF,CAAC;AAOD,aAAO,IAAI,CAAC,KAAK,KAAK,SAAS;AAC7B,cAAM,gBAAgB,MAAM,SAAS,UAAU,iBAAK;AACpD,YAAI,eAAe;AACjB,cAAI,OAAO,QAAQ;AAAA,YACjB,SAAS,EAAE,QAAQ,cAAc;AAAA,UACnC;AAAA,QACF;AACA,aAAK;AAAA,MACP,CAAC;AAED,aAAO,IAAI,aAAAC,QAAM,QAAQ,QAAQ,MAAM,CAAC;AAExC,aAAO,IAAI,yBAAyB,OAAO,KAAK,QAAQ;AACtD,cAAMH,WAAW,IAAY;AAE7B,YAAI,OAAO;AACX,YAAI,QAAQ;AACZ,YAAI,WAAW;AAEf,YAAIA,SAAQ,MAAM,SAAS,OAAO;AAChC,qBAAW,EAAE,OAAOA,SAAQ,MAAM,SAAS,OAAO,MAAM,MAAO;AAAA,QAEjE,OAAO;AACL,gBAAM,OAAuDA,SAAQ,MAAM;AAM3E,cAAIA,SAAQ,MAAM,SAAS,OAAO;AAChC,iBAAK,iBAAiB,MAAM,eAAI,OAAOA,SAAQ,MAAM,SAAS,KAAK;AAAA,UACrE;AAGA,cAAI,KAAK,SAAS;AAChB,iBAAK,UAAU,MAAM,qBAAqB,KAAK,OAAO;AAAA,UACxD;AAEA,iBAAO,MAAM,sBAAsB,MAAMA,SAAQ,MAAM,QAA6B;AACpF,kBAAQ,MAAM,iBAAK,SAAS,gBAAgB,IAAI;AAChD,qBAAW,EAAE,MAAM,MAAM;AAAA,QAC3B;AAMA,YAAI,KAAK,uFAAuF,KAAK,UAAU,QAAQ,CAAC,6CAA6C;AACrK,YAAI,IAAI;AAAA,MACV,CAAC;AAAA,IACH,CAAC;AAED,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,qBAAqB,KAAU;AAC7B,QAAI,IAAI,QAAQ,MAAM,QAAQ,IAAI,IAAI,KAAK,IAAI,KAAK,WAAW,GAAG;AAKhE,aAAO,IAAI,KAAK,CAAC;AAAA,IACnB,OAAO;AAIL,aAAO;AAAA,IACT;AAAA,EACF;AACF;",
  "names": ["express", "RedisStore", "session", "fs", "path", "grant"]
}
