In modern web applications, thirdparty logins have become an important part of the user experience. This article details how to implement GitHub and M...
Vue3 + Go Implement Third-Party OAuth Sign-in (GitHub, Microsoft) Complete Guide
Published: 2025-07-25 (a year ago)
GOGinVue

Vue3 + Go Complete guide to implementing third-party OAuth sign-in (GitHub & Microsoft).

In modern web applications, third-party sign-ins have become an important part of the user experience. This article details how to implement GitHub and Microsoft OAuth sign-in capabilities in Vue3 frontend and Go backend projects.

Project Architecture

  • Frontend: Vue3 + TypeScript + Arco Design
  • Backend: Go + Gin + GORM
  • Database: MySQL
  • OAuth providers: GitHub, Microsoft

1. Principle of OAuth login process

Standard OAuth 2.0 flow

Copy
User > Frontend > Backend Redirect Interface > OAuth Authorization Page > User Authorization > 
OAuth callbacks > backends obtain user information > backends generate JWT > 
Backend redirects to frontend > frontend saves token > jumps to the homepage

Detailed Steps:

  1. The user clicks on the third-party login button
  2. Jump to the backend redirect interface
  3. The backend redirects to the OAuth provider authorization page
  4. User authorization on the OAuth page
  5. The OAuth provider calls back the backend and returns the authorization code
  6. The backend exchanges the authorization code for access_token
  7. The backend uses access_token to obtain user information
  8. Create/update users on the backend and generate JWT tokens
  9. Backend redirects to frontend callback page (with token)
  10. Save the token on the frontend and jump to the target page

2. Back-end implementation

1. Database model design

go Copy
type UserModel struct {
    ID           uint   `gorm:"primaryKey"`
    Username     string
    Nickname     string
    Email        string
    Avatar       string
    Password     string
    
    Third-party login ID (uniformly using string type is compatible with all platforms)
    GithubID     string `gorm:"index"`
    MicrosoftID  string `gorm:"index"`
    
    Role int // User role
    SignStatus int // Login method identifier
    IP           string
    Addr         string
    CreatedAt    time. Time
    UpdatedAt    time. Time
}

2. GitHub OAuth implementation

OAuth configuration

go Copy
type GitHubOAuth struct{}

func (g GitHubOAuth) Config() *oauth2. Config {
    return &oauth2. Config{
        ClientID:     config. GitHub.ClientID,
        ClientSecret: config. GitHub.ClientSecret,
        RedirectURL:  config. GitHub.RedirectURL,
        Scopes:       []string{"user:email"},
        Endpoint:     github. Endpoint,
    }
}

User Information Structure

go Copy
type GithubUserResponse struct {
    ID        int    `json:"id"`
    Login     string `json:"login"`
    Name      string `json:"name"`
    Email     string `json:"email"`
    AvatarURL string `json:"avatar_url"`
}

Redirect interface

go Copy
func GithubOAuthRedirectView(c *gin. Context) {
    config := GitHubOAuth{}. Config()
    url := config. AuthCodeURL("state", oauth2. AccessTypeOnline)
    c.Redirect(http. StatusFound, url)
}

Callback handling

go Copy
func GithubOAuthCallbackView(c *gin. Context) {
    Get an authorization code
    code := c.Query("code")
    if code == "" {
        c.JSON(400, gin. H{"error": "authentication failed"})
        return
    }
    
    Exchange access_token
    config := GitHubOAuth{}. Config()
    token, err := config. Exchange(c, code)
    if err != nil {
        c.JSON(400, gin. H{"error": "authentication failed"})
        return
    }
    
    Get user information
    client := config. Client(c, token)
    resp, err := client. Get("https://api.github.com/user")
    if err != nil {
        c.JSON(400, gin. H{"error": "Failed to obtain user information"})
        return
    }
    defer resp. Body.Close()
    
    body, _ := io. ReadAll(resp. Body)
    var githubUser GithubUserResponse
    if err := json. Unmarshal(body, &githubUser); err != nil {
        c.JSON(400, gin. H{"error": "User information parsing failed"})
        return
    }
    
    Get user IP and geolocation
    userIP := c.ClientIP()
    Geolocation resolution logic...
    
    Find or create a user
    var user UserModel
    err = db. Where("github_id = ?", strconv. Itoa(githubUser.ID)). First(&user). Error
    if err != nil {
        Create a new user
        user = UserModel{
            Username:   githubUser.Login,
            Nickname:   githubUser.Name,
            Avatar:     githubUser.AvatarURL,
            GithubID:   strconv. Itoa(githubUser.ID),
            Email:      githubUser.Email,
            IP:         userIP,
            Role: 2, // Regular user
            SignStatus: 1, // GitHub Login
        }
        
        if err := db. Create(&user). Error; err != nil {
            c.JSON(500, gin. H{"error": "User creation failed"})
            return
        }
    }
    
    Generate a JWT token
    accessToken := generateAccessToken(user)
    refreshToken := generateRefreshToken(user)
    
    Redirects to the front-end callback page
    callbackURL := fmt. Sprintf("http://localhost:3000/login/github/callback?access_token=%s&refresh_token=%s", 
        accessToken, refreshToken)
    c.Redirect(http. StatusFound, callbackURL)
}

3. Microsoft OAuth implementation

OAuth configuration

go Copy
type MicrosoftOAuth struct{}

func (m MicrosoftOAuth) Config() *oauth2. Config {
    return &oauth2. Config{
        ClientID:     config. Microsoft.ClientID,
        ClientSecret: config. Microsoft.ClientSecret,
        RedirectURL:  config. Microsoft.RedirectURL,
        Scopes:       []string{"https://graph.microsoft.com/User.Read"},
        Endpoint: oauth2. Endpoint{
            AuthURL:  "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
            TokenURL: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
        },
    }
}

User Information Structure

go Copy
type MicrosoftUserResponse struct {
    ID                string `json:"id"`
    DisplayName       string `json:"displayName"`
    GivenName         string `json:"givenName"`
    UserPrincipalName string `json:"userPrincipalName"`
    Mail              string `json:"mail"`
}

Callback Handling (Key Differences)

go Copy
func MicrosoftOAuthCallbackView(c *gin. Context) {
    Get the authorization code (note: use Query instead of Param)
    code := c.Query("code")
    if code == "" {
        c.JSON(400, gin. H{"error": "authentication failed"})
        return
    }
    
    Exchange tokens
    config := MicrosoftOAuth{}. Config()
    token, err := config. Exchange(c, code)
    if err != nil {
        c.JSON(400, gin. H{"error": "authentication failed"})
        return
    }
    
    Obtaining user information (using Microsoft Graph APIs)
    client := config. Client(c, token)
    resp, err := client. Get("https://graph.microsoft.com/v1.0/me")
    if err != nil {
        c.JSON(400, gin. H{"error": "Failed to obtain user information"})
        return
    }
    defer resp. Body.Close()
    
    body, _ := io. ReadAll(resp. Body)
    var microsoftUser MicrosoftUserResponse
    if err := json. Unmarshal(body, µsoftUser); err != nil {
        c.JSON(400, gin. H{"error": "User information parsing failed"})
        return
    }
    
    Find or create a user
    var user UserModel
    err = db. Where("microsoft_id = ?", microsoftUser.ID). First(&user). Error
    if err != nil {
        user = UserModel{
            Username:    microsoftUser.UserPrincipalName,
            Nickname:    microsoftUser.DisplayName,
            MicrosoftID: microsoftUser.ID,
            Email:       microsoftUser.Mail,
            Role: 2, // Regular user
            SignStatus: 2, // Microsoft Sign-in
        }
        
        if err := db. Create(&user). Error; err != nil {
            c.JSON(500, gin. H{"error": "User creation failed"})
            return
        }
    }
    
    Generate JWT and redirect
    accessToken := generateAccessToken(user)
    refreshToken := generateRefreshToken(user)
    
    callbackURL := fmt. Sprintf("http://localhost:3000/login/microsoft/callback?access_token=%s&refresh_token=%s", 
        accessToken, refreshToken)
    c.Redirect(http. StatusFound, callbackURL)
}

4. Routing configuration

go Copy
func InitOAuthRoutes(router *gin. RouterGroup) {
    // GitHub OAuth
    router. GET("auth/github", GithubOAuthRedirectView)
    router. GET("auth/github/callback", GithubOAuthCallbackView)
    
    // Microsoft OAuth
    router. GET("auth/microsoft", MicrosoftOAuthRedirectView)
    router. GET("auth/microsoft/callback", MicrosoftOAuthCallbackView)
}

3. Front-end implementation

1. Login form component

vue Copy
<template>
    <div class="login-form">
        <div class="title"> user login</div>

                    <template></template>

                    <template></template>

            login

        <div class="oauth-login">
            <div class="label"> third-party login</div>
            <div class="icons">
                <a>
                    <img src="/images/github.png" alt="GitHub login" >
                </a>
                <a>
                    <img src="/images/microsoft.svg" alt="Microsoft login" >
                </a>
            </div>
        </div>
    </div>
</template>



<style lang="scss">
.oauth-login {
    margin-top: 20px;
    
    .label {
        text-align: center;
        color: #666;
        margin-bottom: 15px;
        position: relative;
        
        &::before, &::after {
            content: "";
            position: absolute;
            top: 50%;
            width: 30%;
            height: 1px;
            background-color: #ddd;
        }
        
        &::before { left: 0; }
        &::after { right: 0; }
    }
    
    .icons {
        display: flex;
        justify-content: center;
        gap: 20px;
        
        img {
            width: 40px;
            height: 40px;
            cursor: pointer;
            transition: transform 0.2s;
            
            &:hover {
                transform: scale(1.1);
            }
        }
    }
}
</style>

2. GitHub callback page

vue Copy
<template>
    <div class="oauth-callback">
        <div class="loading-container">
            
            <p>GitHub login is being processed....</p>
        </div>
    </div>
</template>



<style lang="scss">
.oauth-callback {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100vh;
    
    .loading-container {
        text-align: center;
        
        p {
            margin-top: 16px;
            color: #666;
        }
    }
}
</style>

3. Microsoft callback page

The Microsoft callback page is similar to GitHub, only you need to modify the relevant copy and identifiers:

vue Copy
<template>
    <div class="oauth-callback">
        <div class="loading-container">
            
            <p>Microsoft sign-in is being processed....</p>
        </div>
    </div>
</template>

4. Routing configuration

typescript Copy
import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
    history: createWebHistory(),
    routes: [
        {
            path: "/login",
            name: "login",
            component: () => import('@/views/login/index.vue')
        },
        {
            path: "/login/github/callback",
            name: "github_callback",
            component: () => import('@/views/login/github-callback.vue')
        },
        {
            path: "/login/microsoft/callback",
            name: "microsoft_callback",
            component: () => import('@/views/login/microsoft-callback.vue')
        },
        Other routes...
    ]
})

export default router

5. User status management

typescript Copy
import { defineStore } from 'pinia'

interface UserInfo {
    access_token: string
    refresh_token: string
    user_id: number
    nickname: string
    avatar: string
    role: number
}

export const useUserStore = defineStore('user', {
    state(): { userInfo: UserInfo } {
        return {
            userInfo: {
                access_token: "",
                refresh_token: "",
                user_id: 0,
                nickname: "",
                avatar: "",
                role: 0
            }
        }
    },
    
    actions: {
        setToken(accessToken: string, refreshToken: string) {
            this.userInfo.access_token = accessToken
            this.userInfo.refresh_token = refreshToken
            
            Parse JWT to obtain user information
            const payload = this.parseJWT(accessToken)
            if (payload) {
                this.userInfo.user_id = payload.user_id
                this.userInfo.nickname = payload.nickname
                this.userInfo.avatar = payload.avatar
                this.userInfo.role = payload.role
            }
            
            Persistent storage
            localStorage.setItem("userInfo", JSON.stringify(this.userInfo))
        },
        
        parseJWT(token: string) {
            try {
                const base64Url = token.split('.') [1]
                const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/')
                const jsonPayload = decodeURIComponent(
                    atob(base64).split('').map(c => 
                        '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
                    ).join('')
                )
                return JSON.parse(jsonPayload)
            } catch (error) {
                console.error('JWT parsing failed:', error)
                return null
            }
        },
        
        logout() {
            this.userInfo = {
                access_token: "",
                refresh_token: "",
                user_id: 0,
                nickname: "",
                avatar: "",
                role: 0
            }
            localStorage.removeItem("userInfo")
        }
    },
    
    getters: {
        isLoggedIn(): boolean {
            return !! this.userInfo.access_token && this.userInfo.user_id > 0
        }
    }
})

4. OAuth application configuration

1. GitHub OAuth apps

  1. Visit GitHub Developer Settings (https://github.com/settings/developers)
  2. Click on "New OAuth App"
  3. Fill in the application information:
    • Application name: The name of your app
    • Homepage URL: 'http://localhost:3000' (development environment)
    • Authorization callback URL: http://localhost:8000/api/auth/github/callback
  4. Get the Client ID and Client Secret

2. Microsoft OAuth apps

  1. Access the Azure portal (https://portal.azure.com)
  2. Go to Azure Active Directory → App Registration
  3. Click on "New Registration"
  4. Fill in the application information:
    • Name: The name of your app
    • Supported account types: Accounts and personal Microsoft accounts in any organizational directory
  5. Configure Authentication:
  6. Generate a client key

5. Common problems and solutions

1. redirect_uri Mismatch

Error message: 'The provided value for the input parameter 'redirect_uri' is not valid'

Solution:

  • Ensure that the callback URLs in your OAuth app configuration are exactly the same as your backend code
  • Check whether the protocol (http/https), domain name, port, and path match
  • Notice the difference between 'localhost' and '127.0.0.1'

2. scope permission configuration

GitHub:

  • Basic information: '[]string{"user:email"}'
  • More permissions: '[]string{"user", "user:email"}'

Microsoft:

  • Graph API:[]string{"https://graph.microsoft.com/User.Read"}
  • OpenID Connect:[]string{"openid", "profile", "email"}

3. JSON parsing failed

Make sure the struct's JSON tags match the API return field names:

go Copy
GitHub API return field
type GithubUser struct {
    ID int 'json:"id"' // correct
    AvatarURL string 'json:"avatar_url"' // Note the underscore
}

Microsoft Graph API returns fields  
type MicrosoftUser struct {
    ID string 'json:"id"' // correct
    DisplayName string 'json:"displayName"' // Note the hump naming
}

4. How to obtain an authorization code

The Right Way:

go Copy
code := c.Query("code") // Use Query to get the URL parameter

Wrong Ways:

go Copy
code := c.Param("code") // Error: Param is used for path parameters

5. Frequent request limits

Avoid frequent testing during the development phase:

  • Clear browser cache and cookies
  • Revoke app authorization with the OAuth provider
  • Use a different browser or incognito mode
  • Wait for a while and try again

6. Front-end callback page issues

Make sure the backend redirects to the frontend page instead of returning JSON:

go Copy
Correct: Redirect to frontend
callbackURL := fmt. Sprintf("http://localhost:3000/login/github/callback?access_token=%s", token)
c.Redirect(http. StatusFound, callbackURL)

Error: Returns JSON
c.JSON(200, gin. H{"access_token": token})

6. Safety considerations

1. HTTPS usage

Production environments must use HTTPS:

  • OAuth providers require HTTPS callback URLs
  • Secure token transfers
  • Protection against man-in-the-middle attacks

2. State parameter validation

go Copy
Generate a random state
state := generateRandomString(32)
Save to session or Redis
session. Set("oauth_state", state)

Validation on callback
callbackState := c.Query("state")
sessionState := session. Get("oauth_state")
if callbackState != sessionState {
    c.JSON(400, gin. H{"error": "Status validation failed"})
    return
}

3. Token security

  • Set a reasonable JWT expiration time (e.g., 2 hours)
  • Implement the Refresh Token mechanism
  • Sensitive information is not stored in JWT payload
  • Sign JWTs with strong keys

4. Enter verification

go Copy
Validate the required fields
if user.ID == "" || user. Email == "" {
    c.JSON(400, gin. H{"error": "User information is incomplete"})
    return
}

Mailbox format validation
if !isValidEmail(user. Email) {
    c.JSON(400, gin. H{"error": "Invalid mailbox format"})
    return
}

5. Error handling

Don't expose sensitive information in misinformation:

go Copy
Good practice
c.JSON(400, gin. H{"error": "authentication failed"})

Avoid exposing detailed errors
c.JSON(400, gin. H{"error": fmt. Sprintf("database error: %v", err)})

7. Deployment precautions

1. Environment variable configuration

bash Copy
# GitHub OAuth
GITHUB_CLIENT_ID=your_github_client_id
GITHUB_CLIENT_SECRET=your_github_client_secret
GITHUB_REDIRECT_URL=https://yourdomain.com/api/auth/github/callback

# Microsoft OAuth  
MICROSOFT_CLIENT_ID=your_microsoft_client_id
MICROSOFT_CLIENT_SECRET=your_microsoft_client_secret
MICROSOFT_REDIRECT_URL=https://yourdomain.com/api/auth/microsoft/callback

# JWT key
JWT_SECRET=your_jwt_secret_key

2. Domain name configuration

Production environments need to be updated:

  • The callback URL of the OAuth app
  • The API address in the front-end environment variable
  • The frontend address of the backend redirect

3. CORS configuration

go Copy
func CORSMiddleware() gin. HandlerFunc {
    return gin. HandlerFunc(func(c *gin. Context) {
        c.Header("Access-Control-Allow-Origin", "https://yourdomain.com")
        c.Header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
        c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization")
        
        if c.Request.Method == "OPTIONS" {
            c.AbortWithStatus(204)
            return
        }
        
        c.Next()
    })
}

8. Summary

This article details the complete scenario for Vue3 + Go to implement third-party OAuth sign-in, including:

Key Takeaways:

  1. Understanding the OAuth Process: Master the complete process of authorization code patterns
  2. Backend Implementation: Handle redirects, callbacks, user information acquisition, and JWT generation correctly
  3. Front-end implementation: Clean login interface and callback page handling
  4. Configuration Management: Proper configuration and environment variable management for OAuth applications
  5. Security Considerations: State verification, HTTPS, token security, etc
  6. Error Handling: Well-developed error handling and user-friendly tips

Technical Gains:

  • Practical application of OAuth 2.0 standard processes
  • Authentication scheme under the front-end and back-end separation architecture
  • Generation and validation of JWT tokens
  • Practice of the Vue3 Composition API
  • HTTP handling for the Go Gin framework

This complete implementation allows users to easily sign in to the app with their GitHub or Microsoft account, greatly improving the user experience. In actual projects, support for more OAuth providers can also be expanded as needed, such as Google, WeChat, QQ, etc.

Reference Resources