├── .editorconfig ├── .github └── workflows │ └── build.yml ├── .styleci.yml ├── CHANGELOG.md ├── LICENSE ├── composer.json ├── extend.php ├── js ├── admin.js ├── dist │ ├── admin.js │ ├── admin.js.map │ ├── forum.js │ └── forum.js.map ├── forum.js ├── package-lock.json ├── package.json ├── src │ ├── admin │ │ ├── components │ │ │ └── GithubSettingsModal.js │ │ └── index.js │ └── forum │ │ └── index.js └── webpack.config.js ├── less └── forum.less └── src └── GithubAuthController.php /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.{diff,md}] 16 | trim_trailing_whitespace = false 17 | 18 | [*.php] 19 | indent_size = 4 20 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build JavaScript assets 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@master 14 | - uses: flarum/action-build@master 15 | env: 16 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 17 | 18 | -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: recommended 2 | 3 | enabled: 4 | - logical_not_operators_with_successor_space 5 | 6 | disabled: 7 | - align_double_arrow 8 | - blank_line_after_opening_tag 9 | - multiline_array_trailing_comma 10 | - new_with_braces 11 | - phpdoc_align 12 | - phpdoc_order 13 | - phpdoc_separation 14 | - phpdoc_types 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [0.1.0-beta.13](https://github.com/flarum/auth-github/compare/v0.1.0-beta.12...v0.1.0-beta.13) 4 | 5 | ### Changed 6 | - Updated JS dependencies 7 | 8 | ## [0.1.0-beta.9](https://github.com/flarum/auth-github/compare/v0.1.0-beta.8...v0.1.0-beta.9) 9 | 10 | ### Fixed 11 | - OAuth callback URL was incorrect when Flarum was installed in a subfolder ([6504f74](https://github.com/flarum/auth-github/commit/6504f74170b8c5f7d7afb87b1a1335a583e7fdec)) 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Stichting Flarum (Flarum Foundation) 4 | Copyright (c) 2014-2019 Toby Zerner (toby.zerner@gmail.com) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flarum/auth-github", 3 | "description": "Allow users to log in with GitHub.", 4 | "type": "flarum-extension", 5 | "keywords": ["authentication"], 6 | "license": "MIT", 7 | "support": { 8 | "issues": "https://github.com/flarum/core/issues", 9 | "source": "https://github.com/flarum/auth-github" 10 | }, 11 | "require": { 12 | "flarum/core": "^0.1.0-beta.13", 13 | "league/oauth2-github": "^2.0" 14 | }, 15 | "autoload": { 16 | "psr-4": { 17 | "Flarum\\Auth\\Github\\": "src/" 18 | } 19 | }, 20 | "extra": { 21 | "branch-alias": { 22 | "dev-master": "0.1.x-dev" 23 | }, 24 | "flarum-extension": { 25 | "title": "GitHub Login", 26 | "icon": { 27 | "name": "fab fa-github", 28 | "backgroundColor": "#eee", 29 | "color": "#333" 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /extend.php: -------------------------------------------------------------------------------- 1 | js(__DIR__.'/js/dist/forum.js') 16 | ->css(__DIR__.'/less/forum.less'), 17 | 18 | (new Extend\Frontend('admin')) 19 | ->js(__DIR__.'/js/dist/admin.js'), 20 | 21 | (new Extend\Routes('forum')) 22 | ->get('/auth/github', 'auth.github', GithubAuthController::class), 23 | ]; 24 | -------------------------------------------------------------------------------- /js/admin.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Flarum. 3 | * 4 | * (c) Toby Zerner 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | export * from './src/admin'; 11 | -------------------------------------------------------------------------------- /js/dist/admin.js: -------------------------------------------------------------------------------- 1 | module.exports=function(t){var e={};function r(n){if(e[n])return e[n].exports;var o=e[n]={i:n,l:!1,exports:{}};return t[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=t,r.c=e,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)r.d(n,o,function(e){return t[e]}.bind(null,o));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=5)}([function(t,e){t.exports=flarum.core.compat.app},,,,function(t,e){t.exports=flarum.core.compat["components/SettingsModal"]},function(t,e,r){"use strict";r.r(e);var n=r(0),o=r.n(n);var u=r(4),a=function(t){var e,r;function n(){return t.apply(this,arguments)||this}r=t,(e=n).prototype=Object.create(r.prototype),e.prototype.constructor=e,e.__proto__=r;var o=n.prototype;return o.className=function(){return"GithubSettingsModal Modal--small"},o.title=function(){return app.translator.trans("flarum-auth-github.admin.github_settings.title")},o.form=function(){return[m("div",{className:"Form-group"},m("label",null,app.translator.trans("flarum-auth-github.admin.github_settings.client_id_label")),m("input",{className:"FormControl",bidi:this.setting("flarum-auth-github.client_id")})),m("div",{className:"Form-group"},m("label",null,app.translator.trans("flarum-auth-github.admin.github_settings.client_secret_label")),m("input",{className:"FormControl",bidi:this.setting("flarum-auth-github.client_secret")}))]},n}(r.n(u).a);o.a.initializers.add("flarum-auth-github",(function(){o.a.extensionSettings["flarum-auth-github"]=function(){return o.a.modal.show(new a)}}))}]); 2 | //# sourceMappingURL=admin.js.map -------------------------------------------------------------------------------- /js/dist/admin.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack://@flarum/auth-github/webpack/bootstrap","webpack://@flarum/auth-github/external \"flarum.core.compat['app']\"","webpack://@flarum/auth-github/external \"flarum.core.compat['components/SettingsModal']\"","webpack://@flarum/auth-github/./src/admin/components/GithubSettingsModal.js","webpack://@flarum/auth-github/./node_modules/@babel/runtime/helpers/esm/inheritsLoose.js","webpack://@flarum/auth-github/./src/admin/index.js"],"names":["installedModules","__webpack_require__","moduleId","exports","module","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","flarum","core","compat","GithubSettingsModal","subClass","superClass","constructor","__proto__","className","title","app","translator","trans","form","bidi","this","setting","SettingsModal","initializers","add","extensionSettings","modal","show"],"mappings":"2BACE,IAAIA,EAAmB,GAGvB,SAASC,EAAoBC,GAG5B,GAAGF,EAAiBE,GACnB,OAAOF,EAAiBE,GAAUC,QAGnC,IAAIC,EAASJ,EAAiBE,GAAY,CACzCG,EAAGH,EACHI,GAAG,EACHH,QAAS,IAUV,OANAI,EAAQL,GAAUM,KAAKJ,EAAOD,QAASC,EAAQA,EAAOD,QAASF,GAG/DG,EAAOE,GAAI,EAGJF,EAAOD,QA0Df,OArDAF,EAAoBQ,EAAIF,EAGxBN,EAAoBS,EAAIV,EAGxBC,EAAoBU,EAAI,SAASR,EAASS,EAAMC,GAC3CZ,EAAoBa,EAAEX,EAASS,IAClCG,OAAOC,eAAeb,EAASS,EAAM,CAAEK,YAAY,EAAMC,IAAKL,KAKhEZ,EAAoBkB,EAAI,SAAShB,GACX,oBAAXiB,QAA0BA,OAAOC,aAC1CN,OAAOC,eAAeb,EAASiB,OAAOC,YAAa,CAAEC,MAAO,WAE7DP,OAAOC,eAAeb,EAAS,aAAc,CAAEmB,OAAO,KAQvDrB,EAAoBsB,EAAI,SAASD,EAAOE,GAEvC,GADU,EAAPA,IAAUF,EAAQrB,EAAoBqB,IAC/B,EAAPE,EAAU,OAAOF,EACpB,GAAW,EAAPE,GAA8B,iBAAVF,GAAsBA,GAASA,EAAMG,WAAY,OAAOH,EAChF,IAAII,EAAKX,OAAOY,OAAO,MAGvB,GAFA1B,EAAoBkB,EAAEO,GACtBX,OAAOC,eAAeU,EAAI,UAAW,CAAET,YAAY,EAAMK,MAAOA,IACtD,EAAPE,GAA4B,iBAATF,EAAmB,IAAI,IAAIM,KAAON,EAAOrB,EAAoBU,EAAEe,EAAIE,EAAK,SAASA,GAAO,OAAON,EAAMM,IAAQC,KAAK,KAAMD,IAC9I,OAAOF,GAIRzB,EAAoB6B,EAAI,SAAS1B,GAChC,IAAIS,EAAST,GAAUA,EAAOqB,WAC7B,WAAwB,OAAOrB,EAAgB,SAC/C,WAA8B,OAAOA,GAEtC,OADAH,EAAoBU,EAAEE,EAAQ,IAAKA,GAC5BA,GAIRZ,EAAoBa,EAAI,SAASiB,EAAQC,GAAY,OAAOjB,OAAOkB,UAAUC,eAAe1B,KAAKuB,EAAQC,IAGzG/B,EAAoBkC,EAAI,GAIjBlC,EAAoBA,EAAoBmC,EAAI,G,gBClFrDhC,EAAOD,QAAUkC,OAAOC,KAAKC,OAAY,K,iBCAzCnC,EAAOD,QAAUkC,OAAOC,KAAKC,OAAO,6B,mECEfC,E,YCFN,IAAwBC,EAAUC,E,kDAAAA,E,GAAVD,E,GAC5BR,UAAYlB,OAAOY,OAAOe,EAAWT,WAC9CQ,EAASR,UAAUU,YAAcF,EACjCA,EAASG,UAAYF,E,2BDArBG,U,WACE,MAAO,oC,EAGTC,M,WACE,OAAOC,IAAIC,WAAWC,MAAM,mD,EAG9BC,K,WACE,MAAO,CACL,SAAKL,UAAU,cACb,eAAQE,IAAIC,WAAWC,MAAM,6DAC7B,WAAOJ,UAAU,cAAcM,KAAMC,KAAKC,QAAQ,mCAGpD,SAAKR,UAAU,cACb,eAAQE,IAAIC,WAAWC,MAAM,iEAC7B,WAAOJ,UAAU,cAAcM,KAAMC,KAAKC,QAAQ,yC,UAlBTC,GEEjDP,IAAIQ,aAAaC,IAAI,sBAAsB,WACzCT,IAAIU,kBAAkB,sBAAwB,kBAAMV,IAAIW,MAAMC,KAAK,IAAInB","file":"admin.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 5);\n","module.exports = flarum.core.compat['app'];","module.exports = flarum.core.compat['components/SettingsModal'];","import SettingsModal from 'flarum/components/SettingsModal';\n\nexport default class GithubSettingsModal extends SettingsModal {\n className() {\n return 'GithubSettingsModal Modal--small';\n }\n\n title() {\n return app.translator.trans('flarum-auth-github.admin.github_settings.title');\n }\n\n form() {\n return [\n
\n \n \n
,\n\n
\n \n \n
\n ];\n }\n}\n","export default function _inheritsLoose(subClass, superClass) {\n subClass.prototype = Object.create(superClass.prototype);\n subClass.prototype.constructor = subClass;\n subClass.__proto__ = superClass;\n}","import app from 'flarum/app';\n\nimport GithubSettingsModal from './components/GithubSettingsModal';\n\napp.initializers.add('flarum-auth-github', () => {\n app.extensionSettings['flarum-auth-github'] = () => app.modal.show(new GithubSettingsModal());\n});\n"],"sourceRoot":""} -------------------------------------------------------------------------------- /js/dist/forum.js: -------------------------------------------------------------------------------- 1 | module.exports=function(t){var e={};function n(o){if(e[o])return e[o].exports;var r=e[o]={i:o,l:!1,exports:{}};return t[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=t,n.c=e,n.d=function(t,e,o){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:o})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)n.d(o,r,function(e){return t[e]}.bind(null,r));return o},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=6)}([function(t,e){t.exports=flarum.core.compat.app},function(t,e){t.exports=flarum.core.compat.extend},function(t,e){t.exports=flarum.core.compat["components/LogInButtons"]},function(t,e){t.exports=flarum.core.compat["components/LogInButton"]},,,function(t,e,n){"use strict";n.r(e);var o=n(1),r=n(0),u=n.n(r),a=n(2),i=n.n(a),c=n(3),f=n.n(c);u.a.initializers.add("flarum-auth-github",(function(){Object(o.extend)(i.a.prototype,"items",(function(t){t.add("github",m(f.a,{className:"Button LogInButton--github",icon:"fab fa-github",path:"/auth/github"},u.a.translator.trans("flarum-auth-github.forum.log_in.with_github_button")))}))}))}]); 2 | //# sourceMappingURL=forum.js.map -------------------------------------------------------------------------------- /js/dist/forum.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack://@flarum/auth-github/webpack/bootstrap","webpack://@flarum/auth-github/external \"flarum.core.compat['app']\"","webpack://@flarum/auth-github/external \"flarum.core.compat['extend']\"","webpack://@flarum/auth-github/external \"flarum.core.compat['components/LogInButtons']\"","webpack://@flarum/auth-github/external \"flarum.core.compat['components/LogInButton']\"","webpack://@flarum/auth-github/./src/forum/index.js"],"names":["installedModules","__webpack_require__","moduleId","exports","module","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","flarum","core","compat","app","initializers","add","extend","LogInButtons","items","className","icon","path","translator","trans"],"mappings":"2BACE,IAAIA,EAAmB,GAGvB,SAASC,EAAoBC,GAG5B,GAAGF,EAAiBE,GACnB,OAAOF,EAAiBE,GAAUC,QAGnC,IAAIC,EAASJ,EAAiBE,GAAY,CACzCG,EAAGH,EACHI,GAAG,EACHH,QAAS,IAUV,OANAI,EAAQL,GAAUM,KAAKJ,EAAOD,QAASC,EAAQA,EAAOD,QAASF,GAG/DG,EAAOE,GAAI,EAGJF,EAAOD,QA0Df,OArDAF,EAAoBQ,EAAIF,EAGxBN,EAAoBS,EAAIV,EAGxBC,EAAoBU,EAAI,SAASR,EAASS,EAAMC,GAC3CZ,EAAoBa,EAAEX,EAASS,IAClCG,OAAOC,eAAeb,EAASS,EAAM,CAAEK,YAAY,EAAMC,IAAKL,KAKhEZ,EAAoBkB,EAAI,SAAShB,GACX,oBAAXiB,QAA0BA,OAAOC,aAC1CN,OAAOC,eAAeb,EAASiB,OAAOC,YAAa,CAAEC,MAAO,WAE7DP,OAAOC,eAAeb,EAAS,aAAc,CAAEmB,OAAO,KAQvDrB,EAAoBsB,EAAI,SAASD,EAAOE,GAEvC,GADU,EAAPA,IAAUF,EAAQrB,EAAoBqB,IAC/B,EAAPE,EAAU,OAAOF,EACpB,GAAW,EAAPE,GAA8B,iBAAVF,GAAsBA,GAASA,EAAMG,WAAY,OAAOH,EAChF,IAAII,EAAKX,OAAOY,OAAO,MAGvB,GAFA1B,EAAoBkB,EAAEO,GACtBX,OAAOC,eAAeU,EAAI,UAAW,CAAET,YAAY,EAAMK,MAAOA,IACtD,EAAPE,GAA4B,iBAATF,EAAmB,IAAI,IAAIM,KAAON,EAAOrB,EAAoBU,EAAEe,EAAIE,EAAK,SAASA,GAAO,OAAON,EAAMM,IAAQC,KAAK,KAAMD,IAC9I,OAAOF,GAIRzB,EAAoB6B,EAAI,SAAS1B,GAChC,IAAIS,EAAST,GAAUA,EAAOqB,WAC7B,WAAwB,OAAOrB,EAAgB,SAC/C,WAA8B,OAAOA,GAEtC,OADAH,EAAoBU,EAAEE,EAAQ,IAAKA,GAC5BA,GAIRZ,EAAoBa,EAAI,SAASiB,EAAQC,GAAY,OAAOjB,OAAOkB,UAAUC,eAAe1B,KAAKuB,EAAQC,IAGzG/B,EAAoBkC,EAAI,GAIjBlC,EAAoBA,EAAoBmC,EAAI,G,gBClFrDhC,EAAOD,QAAUkC,OAAOC,KAAKC,OAAY,K,cCAzCnC,EAAOD,QAAUkC,OAAOC,KAAKC,OAAe,Q,cCA5CnC,EAAOD,QAAUkC,OAAOC,KAAKC,OAAO,4B,cCApCnC,EAAOD,QAAUkC,OAAOC,KAAKC,OAAO,2B,iGCKpCC,IAAIC,aAAaC,IAAI,sBAAsB,WACzCC,iBAAOC,IAAaX,UAAW,SAAS,SAASY,GAC/CA,EAAMH,IAAI,SACR,EAAC,IAAD,CACEI,UAAU,6BACVC,KAAK,gBACLC,KAAK,gBACJR,IAAIS,WAAWC,MAAM","file":"forum.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 6);\n","module.exports = flarum.core.compat['app'];","module.exports = flarum.core.compat['extend'];","module.exports = flarum.core.compat['components/LogInButtons'];","module.exports = flarum.core.compat['components/LogInButton'];","import { extend } from 'flarum/extend';\nimport app from 'flarum/app';\nimport LogInButtons from 'flarum/components/LogInButtons';\nimport LogInButton from 'flarum/components/LogInButton';\n\napp.initializers.add('flarum-auth-github', () => {\n extend(LogInButtons.prototype, 'items', function(items) {\n items.add('github',\n \n {app.translator.trans('flarum-auth-github.forum.log_in.with_github_button')}\n \n );\n });\n});\n"],"sourceRoot":""} -------------------------------------------------------------------------------- /js/forum.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Flarum. 3 | * 4 | * (c) Toby Zerner 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | export * from './src/forum'; 11 | -------------------------------------------------------------------------------- /js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "@flarum/auth-github", 4 | "dependencies": { 5 | "flarum-webpack-config": "0.1.0-beta.10", 6 | "webpack": "^4.43.0", 7 | "webpack-cli": "^3.3.11" 8 | }, 9 | "scripts": { 10 | "dev": "webpack --mode development --watch", 11 | "build": "webpack --mode production" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /js/src/admin/components/GithubSettingsModal.js: -------------------------------------------------------------------------------- 1 | import SettingsModal from 'flarum/components/SettingsModal'; 2 | 3 | export default class GithubSettingsModal extends SettingsModal { 4 | className() { 5 | return 'GithubSettingsModal Modal--small'; 6 | } 7 | 8 | title() { 9 | return app.translator.trans('flarum-auth-github.admin.github_settings.title'); 10 | } 11 | 12 | form() { 13 | return [ 14 |
15 | 16 | 17 |
, 18 | 19 |
20 | 21 | 22 |
23 | ]; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /js/src/admin/index.js: -------------------------------------------------------------------------------- 1 | import app from 'flarum/app'; 2 | 3 | import GithubSettingsModal from './components/GithubSettingsModal'; 4 | 5 | app.initializers.add('flarum-auth-github', () => { 6 | app.extensionSettings['flarum-auth-github'] = () => app.modal.show(new GithubSettingsModal()); 7 | }); 8 | -------------------------------------------------------------------------------- /js/src/forum/index.js: -------------------------------------------------------------------------------- 1 | import { extend } from 'flarum/extend'; 2 | import app from 'flarum/app'; 3 | import LogInButtons from 'flarum/components/LogInButtons'; 4 | import LogInButton from 'flarum/components/LogInButton'; 5 | 6 | app.initializers.add('flarum-auth-github', () => { 7 | extend(LogInButtons.prototype, 'items', function(items) { 8 | items.add('github', 9 | 13 | {app.translator.trans('flarum-auth-github.forum.log_in.with_github_button')} 14 | 15 | ); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /js/webpack.config.js: -------------------------------------------------------------------------------- 1 | const config = require('flarum-webpack-config'); 2 | 3 | module.exports = config(); 4 | -------------------------------------------------------------------------------- /less/forum.less: -------------------------------------------------------------------------------- 1 | .LogInButton--github { 2 | .Button--color(#333, #ccc); 3 | } 4 | -------------------------------------------------------------------------------- /src/GithubAuthController.php: -------------------------------------------------------------------------------- 1 | response = $response; 50 | $this->settings = $settings; 51 | $this->url = $url; 52 | } 53 | 54 | /** 55 | * @param Request $request 56 | * @return ResponseInterface 57 | * @throws Exception 58 | */ 59 | public function handle(Request $request): ResponseInterface 60 | { 61 | $redirectUri = $this->url->to('forum')->route('auth.github'); 62 | 63 | $provider = new Github([ 64 | 'clientId' => $this->settings->get('flarum-auth-github.client_id'), 65 | 'clientSecret' => $this->settings->get('flarum-auth-github.client_secret'), 66 | 'redirectUri' => $redirectUri 67 | ]); 68 | 69 | $session = $request->getAttribute('session'); 70 | $queryParams = $request->getQueryParams(); 71 | 72 | $code = array_get($queryParams, 'code'); 73 | 74 | if (! $code) { 75 | $authUrl = $provider->getAuthorizationUrl(['scope' => ['user:email']]); 76 | $session->put('oauth2state', $provider->getState()); 77 | 78 | return new RedirectResponse($authUrl.'&display=popup'); 79 | } 80 | 81 | $state = array_get($queryParams, 'state'); 82 | 83 | if (! $state || $state !== $session->get('oauth2state')) { 84 | $session->remove('oauth2state'); 85 | 86 | throw new Exception('Invalid state'); 87 | } 88 | 89 | $token = $provider->getAccessToken('authorization_code', compact('code')); 90 | 91 | /** @var GithubResourceOwner $user */ 92 | $user = $provider->getResourceOwner($token); 93 | 94 | return $this->response->make( 95 | 'github', 96 | $user->getId(), 97 | function (Registration $registration) use ($user, $provider, $token) { 98 | $registration 99 | ->provideTrustedEmail($user->getEmail() ?: $this->getEmailFromApi($provider, $token)) 100 | ->provideAvatar(array_get($user->toArray(), 'avatar_url')) 101 | ->suggestUsername($user->getNickname()) 102 | ->setPayload($user->toArray()); 103 | } 104 | ); 105 | } 106 | 107 | private function getEmailFromApi(Github $provider, AccessToken $token) 108 | { 109 | $url = $provider->apiDomain.'/user/emails'; 110 | 111 | $response = $provider->getResponse( 112 | $provider->getAuthenticatedRequest('GET', $url, $token) 113 | ); 114 | 115 | $emails = json_decode($response->getBody(), true); 116 | 117 | foreach ($emails as $email) { 118 | if ($email['primary'] && $email['verified']) { 119 | return $email['email']; 120 | } 121 | } 122 | } 123 | } 124 | --------------------------------------------------------------------------------