diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml
index ba73a5d..87ab406 100644
--- a/.github/workflows/ci_cd.yml
+++ b/.github/workflows/ci_cd.yml
@@ -40,6 +40,8 @@ jobs:
env:
GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }}
GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }}
+ GITHUB_CLIENT_ID: ${{ secrets.GITHUB_CLIENT_ID }}
+ GITHUB_CLIENT_SECRET: ${{ secrets.GITHUB_CLIENT_SECRET }}
run: docker-compose -f docker-compose.testing.yml run cypress
- name: Tear Down Test Environment
diff --git a/app/src/components/AuthenticationPart.vue b/app/src/components/AuthenticationPart.vue
index 8ac1a14..3f2126a 100644
--- a/app/src/components/AuthenticationPart.vue
+++ b/app/src/components/AuthenticationPart.vue
@@ -191,6 +191,13 @@ const goBack = () => {
>
Google
+
+ GitHub
+
diff --git a/app/tailwind.config.js b/app/tailwind.config.js
index 6864fdc..14cfd55 100644
--- a/app/tailwind.config.js
+++ b/app/tailwind.config.js
@@ -24,6 +24,7 @@ export default {
},
icon: {
google: '#DB4437',
+ github: '#171515',
},
},
},
diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml
index 006532b..1883512 100644
--- a/docker-compose.prod.yml
+++ b/docker-compose.prod.yml
@@ -44,6 +44,8 @@ services:
- MONGO_DB=odyssey-test
- GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID}
- GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET}
+ - GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID}
+ - GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET}
base:
image: lancatlin/linux-odyssey:base
diff --git a/docker-compose.testing.yml b/docker-compose.testing.yml
index 46ea3b0..31b78cf 100644
--- a/docker-compose.testing.yml
+++ b/docker-compose.testing.yml
@@ -42,6 +42,8 @@ services:
- DOCKER_NETWORK=linux-odyssey-players
- GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID}
- GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET}
+ - GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID}
+ - GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET}
db-test:
image: mongo
diff --git a/docker-compose.yml b/docker-compose.yml
index e787faf..195f55f 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -33,6 +33,8 @@ services:
- HOST_PWD=${PWD}
- GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID}
- GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET}
+ - GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID}
+ - GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET}
db:
image: mongo
networks:
diff --git a/example.env b/example.env
index 17418ff..03f5ea8 100644
--- a/example.env
+++ b/example.env
@@ -1,2 +1,5 @@
+BASE_URL=https://example.com
GOOGLE_CLIENT_ID=
-GOOGLE_CLIENT_SECRET=
\ No newline at end of file
+GOOGLE_CLIENT_SECRET=
+GITHUB_CLIENT_ID=
+GITNUB_CLIENT_SECRET=
diff --git a/server/package.json b/server/package.json
index b88362a..317b6bb 100644
--- a/server/package.json
+++ b/server/package.json
@@ -22,6 +22,7 @@
"mongoose": "^7.4.0",
"node-schedule": "^2.1.1",
"passport": "^0.6.0",
+ "passport-github2": "^0.1.12",
"passport-google-oidc": "^0.1.0",
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0",
diff --git a/server/src/api/routes/auth.js b/server/src/api/routes/auth.js
index d94c3c0..4687610 100644
--- a/server/src/api/routes/auth.js
+++ b/server/src/api/routes/auth.js
@@ -43,4 +43,15 @@ if (enabledMethods.google) {
)
}
+if (enabledMethods.github) {
+ router.get('/github', passport.authenticate('github'))
+ router.get(
+ '/github/callback',
+ passport.authenticate('github', {
+ successRedirect: '/',
+ failureRedirect: '/login',
+ })
+ )
+}
+
export default router
diff --git a/server/src/auth/githubStrategy.js b/server/src/auth/githubStrategy.js
new file mode 100644
index 0000000..7b2120a
--- /dev/null
+++ b/server/src/auth/githubStrategy.js
@@ -0,0 +1,53 @@
+import GitHubStrategy from 'passport-github2'
+
+import config from '../config.js'
+import User from '../models/user.js'
+
+function verify(accessToken, refreshToken, profile, cb) {
+ process.nextTick(async () => {
+ console.log(accessToken, refreshToken, profile)
+ // cb('todo')
+ const email = profile.emails[0]?.value
+ if (!email) {
+ cb(new Error('No email found'))
+ return
+ }
+
+ let user = await User.findOne({ 'github.id': profile.id })
+ if (!user) {
+ user = await User.findOne({ email })
+ const github = {
+ id: profile.id,
+ displayName: profile.displayName,
+ }
+ if (!user) {
+ user = new User({
+ email,
+ github,
+ })
+ } else if (user.github?.id) {
+ cb(new Error('Email already used and binded to another account'))
+ return
+ } else {
+ user.github = github
+ }
+ await user.save()
+ }
+
+ cb(null, user)
+ })
+}
+
+const enabled = config.github.clientID && config.github.clientSecret
+
+export default enabled
+ ? new GitHubStrategy(
+ {
+ clientID: config.github.clientID,
+ clientSecret: config.github.clientSecret,
+ callbackURL: `${config.baseUrl}/api/v1/auth/github/callback`,
+ scope: ['user:email'],
+ },
+ verify
+ )
+ : null
diff --git a/server/src/auth/passport.js b/server/src/auth/passport.js
index e2f4525..9cd9011 100644
--- a/server/src/auth/passport.js
+++ b/server/src/auth/passport.js
@@ -2,6 +2,7 @@ import passport from 'passport'
import passwordStrategy from './passwordStrategy.js'
import jwtStrategy from './jwtStrategy.js'
import googleStrategy from './googleStrategy.js'
+import githubStrategy from './githubStrategy.js'
passport.use(passwordStrategy)
passport.use(jwtStrategy)
@@ -9,6 +10,7 @@ passport.use(jwtStrategy)
const enabledMethods = {
local: true,
google: googleStrategy !== null,
+ github: githubStrategy !== null,
}
console.log('Enabled login methods', enabledMethods)
@@ -17,6 +19,10 @@ if (enabledMethods.google) {
passport.use(googleStrategy)
}
+if (enabledMethods.github) {
+ passport.use(githubStrategy)
+}
+
passport.serializeUser((user, done) => {
process.nextTick(() => {
done(null, {
diff --git a/server/src/config.js b/server/src/config.js
index 9dd4f47..e987cf4 100644
--- a/server/src/config.js
+++ b/server/src/config.js
@@ -14,6 +14,10 @@ const config = {
clientID: get('GOOGLE_CLIENT_ID', ''),
clientSecret: get('GOOGLE_CLIENT_SECRET', ''),
},
+ github: {
+ clientID: get('GITHUB_CLIENT_ID', ''),
+ clientSecret: get('GITHUB_CLIENT_SECRET', ''),
+ },
}
config.baseUrl = get('BASE_URL', `http://${config.host}:${config.port}`)
diff --git a/server/src/models/user.js b/server/src/models/user.js
index 1c3b891..5d910c7 100644
--- a/server/src/models/user.js
+++ b/server/src/models/user.js
@@ -17,6 +17,10 @@ const User = model('User', {
id: String,
displayName: String,
},
+ github: {
+ id: String,
+ displayName: String,
+ },
})
export default User
diff --git a/yarn.lock b/yarn.lock
index 40e7f1d..8465be9 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1448,6 +1448,11 @@ base64id@2.0.0, base64id@~2.0.0:
resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6"
integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==
+base64url@3.x.x:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d"
+ integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==
+
bcrypt-pbkdf@^1.0.0, bcrypt-pbkdf@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
@@ -4664,6 +4669,13 @@ parseurl@~1.3.3:
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
+passport-github2@^0.1.12:
+ version "0.1.12"
+ resolved "https://registry.yarnpkg.com/passport-github2/-/passport-github2-0.1.12.tgz#a72ebff4fa52a35bc2c71122dcf470d1116f772c"
+ integrity sha512-3nPUCc7ttF/3HSP/k9sAXjz3SkGv5Nki84I05kSQPo01Jqq1NzJACgMblCK0fGcv9pKCG/KXU3AJRDGLqHLoIw==
+ dependencies:
+ passport-oauth2 "1.x.x"
+
passport-google-oidc@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/passport-google-oidc/-/passport-google-oidc-0.1.0.tgz#585790ba03864c0238cb89912d7e2a958086b5b0"
@@ -4686,6 +4698,17 @@ passport-local@^1.0.0:
dependencies:
passport-strategy "1.x.x"
+passport-oauth2@1.x.x:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/passport-oauth2/-/passport-oauth2-1.7.0.tgz#5c4766c8531ac45ffe9ec2c09de9809e2c841fc4"
+ integrity sha512-j2gf34szdTF2Onw3+76alNnaAExlUmHvkc7cL+cmaS5NzHzDP/BvFHJruueQ9XAeNOdpI+CH+PWid8RA7KCwAQ==
+ dependencies:
+ base64url "3.x.x"
+ oauth "0.9.x"
+ passport-strategy "1.x.x"
+ uid2 "0.0.x"
+ utils-merge "1.x.x"
+
passport-openidconnect@0.1.x:
version "0.1.1"
resolved "https://registry.yarnpkg.com/passport-openidconnect/-/passport-openidconnect-0.1.1.tgz#83921ff5f87f634079f65262dada834af1972244"
@@ -5775,6 +5798,11 @@ uid-safe@~2.1.5:
dependencies:
random-bytes "~1.0.0"
+uid2@0.0.x:
+ version "0.0.4"
+ resolved "https://registry.yarnpkg.com/uid2/-/uid2-0.0.4.tgz#033f3b1d5d32505f5ce5f888b9f3b667123c0a44"
+ integrity sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==
+
unbox-primitive@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e"
@@ -5838,7 +5866,7 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2:
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
-utils-merge@1.0.1, utils-merge@^1.0.1:
+utils-merge@1.0.1, utils-merge@1.x.x, utils-merge@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==