diff --git a/explorer/next-auth.d.ts b/explorer/next-auth.d.ts
index 59a321891..fd3dbf8dd 100644
--- a/explorer/next-auth.d.ts
+++ b/explorer/next-auth.d.ts
@@ -11,6 +11,7 @@ declare module 'next-auth' {
DIDs: string[]
subspace?: SubspaceToken
discord?: DiscordToken
+ github?: GitHubToken
}
export interface Session {
@@ -31,6 +32,7 @@ declare module 'next-auth/client' {
DIDs: string[]
subspace?: SubspaceToken
discord?: DiscordToken
+ github?: GitHubToken
}
export interface Session {
@@ -46,6 +48,7 @@ declare module 'next-auth/jwt' {
DIDs: string[]
subspace?: SubspaceToken
discord?: DiscordToken
+ github?: GitHubToken
}
interface JWT {
@@ -53,5 +56,6 @@ declare module 'next-auth/jwt' {
DIDs: string[]
subspace?: SubspaceToken
discord?: DiscordToken
+ github?: GitHubToken
}
}
diff --git a/explorer/src/components/WalletSideKick/Actions/ConnectGitHub.tsx b/explorer/src/components/WalletSideKick/Actions/ConnectGitHub.tsx
new file mode 100644
index 000000000..dd38a3f2d
--- /dev/null
+++ b/explorer/src/components/WalletSideKick/Actions/ConnectGitHub.tsx
@@ -0,0 +1,31 @@
+import { StyledListItem } from 'components/common/List'
+import { StyledButton } from 'components/common/StyledButton'
+import { CheckMarkIcon } from 'components/icons/CheckMarkIcon'
+import { signIn, useSession } from 'next-auth/react'
+import { FC, useCallback } from 'react'
+
+export const ConnectGitHub: FC = () => {
+ const { data: session } = useSession()
+
+ const handleConnectGitHub = useCallback(
+ async () => await signIn('github', { redirect: false }),
+ [],
+ )
+
+ if (!session || !session.user) return null
+
+ return (
+
+ {session?.user?.github?.vcs ? (
+ <>
+
+
+ Refresh
+
+ >
+ ) : (
+ Connect
+ )}
+
+ )
+}
diff --git a/explorer/src/components/WalletSideKick/GetDiscordRoles.tsx b/explorer/src/components/WalletSideKick/GetDiscordRoles.tsx
index 5fbcbcb6f..aa1e061a4 100644
--- a/explorer/src/components/WalletSideKick/GetDiscordRoles.tsx
+++ b/explorer/src/components/WalletSideKick/GetDiscordRoles.tsx
@@ -7,6 +7,7 @@ import Link from 'next/link'
import { FC, useMemo, useState } from 'react'
// import { ClaimStakingToken } from './Actions/ClaimStakingToken'
import { ConnectDiscord } from './Actions/ConnectDiscord'
+import { ConnectGitHub } from './Actions/ConnectGitHub'
import { JoinDiscord } from './Actions/JoinDiscord'
import { VerifyWalletOwnership } from './Actions/VerifyWalletOwnership'
@@ -85,6 +86,7 @@ export const GetDiscordRoles: FC = () => {
)}
+
{/* */}
@@ -99,6 +101,7 @@ export const GetDiscordRoles: FC = () => {
+
diff --git a/explorer/src/constants/session.ts b/explorer/src/constants/session.ts
index c10e3af71..a65fa5669 100644
--- a/explorer/src/constants/session.ts
+++ b/explorer/src/constants/session.ts
@@ -1,4 +1,4 @@
-import type { DiscordToken, SubspaceToken } from 'types/jwt'
+import type { DiscordToken, GitHubToken, SubspaceToken } from 'types/jwt'
export const TOKEN_EXPIRATION = 60 * 60 * 24 // 1 day
@@ -34,3 +34,8 @@ export const DEFAULT_DISCORD_TOKEN: DiscordToken = {
},
},
}
+
+export const DEFAULT_GITHUB_TOKEN: GitHubToken = {
+ id: '',
+ username: '',
+}
diff --git a/explorer/src/types/jwt.ts b/explorer/src/types/jwt.ts
index 2798c51e2..eab532dba 100644
--- a/explorer/src/types/jwt.ts
+++ b/explorer/src/types/jwt.ts
@@ -24,3 +24,8 @@ export type DiscordToken = {
}
}
}
+
+export type GitHubToken = {
+ id: string
+ username: string
+}
diff --git a/explorer/src/utils/auth/authOptions.ts b/explorer/src/utils/auth/authOptions.ts
index 2898508de..0932524c9 100644
--- a/explorer/src/utils/auth/authOptions.ts
+++ b/explorer/src/utils/auth/authOptions.ts
@@ -27,6 +27,7 @@ export const authOptions: AuthOptions = {
token.DIDs = user.DIDs
token.subspace = user.subspace
token.discord = user.discord
+ token.github = user.github
}
return token
},
diff --git a/explorer/src/utils/auth/providers/discord.ts b/explorer/src/utils/auth/providers/discord.ts
index 23a915a3d..e149f5228 100644
--- a/explorer/src/utils/auth/providers/discord.ts
+++ b/explorer/src/utils/auth/providers/discord.ts
@@ -1,4 +1,4 @@
-import { AuthProvider } from 'constants/session'
+import { AuthProvider, DEFAULT_GITHUB_TOKEN } from 'constants/session'
import type { TokenSet } from 'next-auth'
import { User } from 'next-auth'
import type { DiscordProfile } from 'next-auth/providers/discord'
@@ -82,6 +82,7 @@ export const Discord = () => {
},
},
},
+ github: session.github || DEFAULT_GITHUB_TOKEN,
}
if (!savedUser || savedUser.length === 0) {
diff --git a/explorer/src/utils/auth/providers/github.ts b/explorer/src/utils/auth/providers/github.ts
new file mode 100644
index 000000000..7416e7d84
--- /dev/null
+++ b/explorer/src/utils/auth/providers/github.ts
@@ -0,0 +1,46 @@
+import * as jsonwebtoken from 'jsonwebtoken'
+import type { TokenSet } from 'next-auth'
+import { JWT } from 'next-auth/jwt'
+import GitHubProvider, { GithubProfile } from 'next-auth/providers/github'
+import { cookies } from 'next/headers'
+
+const { GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET } = process.env
+
+export const GitHub = () => {
+ return GitHubProvider({
+ // client credentials
+ clientId: GITHUB_CLIENT_ID || '',
+ clientSecret: GITHUB_CLIENT_SECRET || '',
+
+ // fetch discord profile
+ profile: async (profile: GithubProfile, token: TokenSet) => {
+ try {
+ if (!token.access_token) throw new Error('No access token')
+
+ if (!process.env.NEXTAUTH_SECRET) throw new Error('No secret')
+ const { NEXTAUTH_SECRET } = process.env
+
+ const { get } = cookies()
+ const sessionToken = get('next-auth.session-token')?.value || ''
+ const session = jsonwebtoken.verify(sessionToken, NEXTAUTH_SECRET, {
+ algorithms: ['HS256'],
+ }) as JWT
+ const did = 'did:openid:github:' + profile.id
+
+ return {
+ id: session.id || did,
+ DIDs: [...session.DIDs, did],
+ subspace: session.subspace,
+ discord: session.discord,
+ github: {
+ id: profile.id,
+ username: profile.login,
+ },
+ }
+ } catch (error) {
+ console.error('Error fetching Discord profile:', error)
+ throw new Error('Failed to fetch Discord profile')
+ }
+ },
+ })
+}
diff --git a/explorer/src/utils/auth/providers/index.ts b/explorer/src/utils/auth/providers/index.ts
index 5a7410ca0..17c7a07e5 100644
--- a/explorer/src/utils/auth/providers/index.ts
+++ b/explorer/src/utils/auth/providers/index.ts
@@ -1,6 +1,7 @@
import { Provider } from 'next-auth/providers'
import { Discord } from './discord'
+import { GitHub } from './github'
import { Nova } from './nova'
import { Subspace } from './subspace'
-export const providers: Provider[] = [Discord(), Subspace(), Nova()]
+export const providers: Provider[] = [Discord(), GitHub(), Subspace(), Nova()]
diff --git a/explorer/src/utils/auth/providers/subspace.ts b/explorer/src/utils/auth/providers/subspace.ts
index dd61389ac..72e14606a 100644
--- a/explorer/src/utils/auth/providers/subspace.ts
+++ b/explorer/src/utils/auth/providers/subspace.ts
@@ -1,5 +1,5 @@
-import { cryptoWaitReady, signatureVerify } from '@autonomys/auto-utils'
-import { AuthProvider, DEFAULT_DISCORD_TOKEN } from 'constants/session'
+import { cryptoWaitReady, signatureVerify } from '@polkadot/util-crypto'
+import { AuthProvider, DEFAULT_DISCORD_TOKEN, DEFAULT_GITHUB_TOKEN } from 'constants/session'
import { User } from 'next-auth'
import type { Provider } from 'next-auth/providers'
import CredentialsProvider from 'next-auth/providers/credentials'
@@ -55,6 +55,7 @@ export const Subspace = () => {
},
},
discord: DEFAULT_DISCORD_TOKEN,
+ github: DEFAULT_GITHUB_TOKEN,
}
if (!savedUser || savedUser.length === 0) {