Overview
Implement GitHub account connection functionality for users who are authenticated via Reown. This will enable seamless integration between DappyKit and GitHub services while maintaining Web3 authentication flow.
Requirements
Maintain Reown as primary authentication method
Allow secure GitHub account linking
Handle OAuth flow with GitHub
Store and manage GitHub access tokens securely
Provide connection status and management UI
Technical Implementation
1. OAuth Configuration
GitHub OAuth App Setup
interface GitHubOAuthConfig {
clientId : string ;
clientSecret : string ;
redirectUri : string ;
scope : string [ ] ;
}
const githubOAuthConfig : GitHubOAuthConfig = {
clientId : process . env . GITHUB_CLIENT_ID ,
clientSecret : process . env . GITHUB_CLIENT_SECRET ,
redirectUri : `${ process . env . APP_URL } /api/auth/github/callback` ,
scope : [ 'read:user' , 'user:email' , 'repo' ]
} ;
2. Backend Implementation
User Model Extension
interface UserGitHubConnection {
githubId : string ;
accessToken : string ;
refreshToken : string ;
scope : string [ ] ;
connectedAt : Date ;
lastUsed : Date ;
}
interface User {
// ... existing Reown user fields ...
githubConnection ?: UserGitHubConnection ;
}
Connection Controller
/**
* Handles GitHub OAuth connection for Reown authenticated users
*/
class GitHubConnectionController {
/**
* Initiates GitHub OAuth flow
* @param req - Express request with Reown user
* @param res - Express response
*/
async initiateConnection ( req : Request , res : Response ) : Promise < void > {
try {
const { user } = req ;
const state = generateSecureState ( user . id ) ;
const authUrl = new URL ( 'https://github.com/login/oauth/authorize' ) ;
authUrl . searchParams . append ( 'client_id' , githubOAuthConfig . clientId ) ;
authUrl . searchParams . append ( 'redirect_uri' , githubOAuthConfig . redirectUri ) ;
authUrl . searchParams . append ( 'scope' , githubOAuthConfig . scope . join ( ' ' ) ) ;
authUrl . searchParams . append ( 'state' , state ) ;
res . redirect ( authUrl . toString ( ) ) ;
} catch ( error ) {
res . status ( 500 ) . json ( {
error : 'Failed to initiate GitHub connection'
} ) ;
}
}
/**
* Handles OAuth callback and completes connection
* @param req - Express request with OAuth code
* @param res - Express response
*/
async handleCallback ( req : Request , res : Response ) : Promise < void > {
try {
const { code, state } = req . query ;
const userId = verifyAndExtractState ( state as string ) ;
// Exchange code for tokens
const tokens = await exchangeCodeForTokens ( code as string ) ;
// Get GitHub user info
const githubUser = await fetchGitHubUserInfo ( tokens . access_token ) ;
// Store connection
await this . storeGitHubConnection ( userId , {
githubId : githubUser . id ,
accessToken : tokens . access_token ,
refreshToken : tokens . refresh_token ,
scope : tokens . scope . split ( ',' ) ,
connectedAt : new Date ( ) ,
lastUsed : new Date ( )
} ) ;
res . redirect ( '/settings/connections' ) ;
} catch ( error ) {
res . status ( 500 ) . json ( {
error : 'Failed to complete GitHub connection'
} ) ;
}
}
}
Token Management
/**
* Manages GitHub access tokens
*/
class GitHubTokenManager {
/**
* Refreshes GitHub access token
* @param userId - Reown user ID
* @returns Updated GitHub connection
*/
async refreshToken ( userId : string ) : Promise < UserGitHubConnection > {
const user = await getUserById ( userId ) ;
const { refreshToken } = user . githubConnection ;
const newTokens = await this . exchangeRefreshToken ( refreshToken ) ;
const updatedConnection = {
...user . githubConnection ,
accessToken : newTokens . access_token ,
refreshToken : newTokens . refresh_token ,
lastUsed : new Date ( )
} ;
await this . updateGitHubConnection ( userId , updatedConnection ) ;
return updatedConnection ;
}
}
3. Frontend Implementation
Connection Component
/**
* GitHub connection management component
*/
const GitHubConnection : React . FC = ( ) => {
const { user } = useReownAuth ( ) ;
const [ isConnected , setIsConnected ] = useState ( false ) ;
useEffect ( ( ) => {
setIsConnected ( ! ! user ?. githubConnection ) ;
} , [ user ] ) ;
const handleConnect = async ( ) => {
try {
window . location . href = '/api/auth/github/connect' ;
} catch ( error ) {
console . error ( 'Failed to initiate GitHub connection:' , error ) ;
}
} ;
const handleDisconnect = async ( ) => {
try {
await axios . post ( '/api/auth/github/disconnect' ) ;
setIsConnected ( false ) ;
} catch ( error ) {
console . error ( 'Failed to disconnect GitHub:' , error ) ;
}
} ;
return (
< div className = "github-connection" >
< h2 > GitHub Connection < / h 2 >
{ isConnected ? (
< div >
< p > Connected to GitHub < / p >
< button onClick = { handleDisconnect} >
Disconnect GitHub
< / b u t t o n >
< / div >
) : (
< button onClick = { handleConnect} >
Connect GitHub Account
< / b u t t o n >
) }
< / div >
) ;
} ;
Connection Status Hook
/**
* Custom hook for GitHub connection status
*/
const useGitHubConnection = ( ) => {
const { user } = useReownAuth ( ) ;
const [ connectionStatus , setConnectionStatus ] = useState ( {
isConnected : false ,
lastUsed : null ,
scopes : [ ]
} ) ;
useEffect ( ( ) => {
if ( user ?. githubConnection ) {
setConnectionStatus ( {
isConnected : true ,
lastUsed : user . githubConnection . lastUsed ,
scopes : user . githubConnection . scope
} ) ;
}
} , [ user ] ) ;
return connectionStatus ;
} ;
4. API Routes
Connection Routes
router . get ( '/api/auth/github/connect' ,
authenticateReown ,
githubController . initiateConnection
) ;
router . get ( '/api/auth/github/callback' ,
authenticateReown ,
githubController . handleCallback
) ;
router . post ( '/api/auth/github/disconnect' ,
authenticateReown ,
githubController . disconnectGitHub
) ;
5. Security Measures
Token Encryption
/**
* Encrypts GitHub tokens before storage
*/
const encryptToken = ( token : string ) : string => {
const cipher = crypto . createCipheriv (
'aes-256-gcm' ,
process . env . ENCRYPTION_KEY ,
process . env . ENCRYPTION_IV
) ;
return cipher . update ( token , 'utf8' , 'hex' ) ;
} ;
State Validation
/**
* Generates and validates OAuth state
*/
const generateSecureState = ( userId : string ) : string => {
const payload = {
userId,
timestamp : Date . now ( )
} ;
return jwt . sign ( payload , process . env . JWT_SECRET , { expiresIn : '5m' } ) ;
} ;
Database Schema Updates
-- Add GitHub connection table
CREATE TABLE user_github_connections (
user_id VARCHAR (255 ) PRIMARY KEY ,
github_id VARCHAR (255 ) UNIQUE,
access_token TEXT ,
refresh_token TEXT ,
scope TEXT [],
connected_at TIMESTAMP ,
last_used TIMESTAMP ,
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- Add indexes
CREATE INDEX idx_github_id ON user_github_connections(github_id);
CREATE INDEX idx_connected_at ON user_github_connections(connected_at);
Security Considerations
Token Storage
Encrypt tokens at rest
Implement token rotation
Regular token validation
Authentication Flow
Validate OAuth state
Implement PKCE
Rate limiting
Scope Management
Minimal required permissions
Scope validation
Regular scope audits
Next Steps
Review and approve technical design
Set up GitHub OAuth application
Implement backend services
Create frontend components
Deploy to staging
Security audit
Production deployment
Please review and provide feedback on this implementation plan.
Overview
Implement GitHub account connection functionality for users who are authenticated via Reown. This will enable seamless integration between DappyKit and GitHub services while maintaining Web3 authentication flow.
Requirements
Technical Implementation
1. OAuth Configuration
2. Backend Implementation
3. Frontend Implementation
4. API Routes
5. Security Measures
Database Schema Updates
Security Considerations
Token Storage
Authentication Flow
Scope Management
Next Steps
Please review and provide feedback on this implementation plan.