Skip to main content

Overview

suSHi supports OAuth 2.0 authentication with Google and GitHub, allowing you to sign in using your existing accounts without creating separate credentials. Benefits:
  • No password to remember
  • Leverages battle-tested authentication providers
  • Easy account linking and recovery
  • Seamless single sign-on experience

Supported Providers

Google OAuth

Sign in with your Google account (@gmail.com or Google Workspace)

GitHub OAuth

Sign in with your GitHub account (personal or organization)

How OAuth Works

suSHi implements the OAuth 2.0 authorization code flow:
suSHi never sees your OAuth provider password. Authentication happens directly with Google or GitHub.

Google OAuth

Authorization Flow

When you sign in with Google:
  1. Redirect to Google: You’re sent to Google’s OAuth authorization page
  2. Permission Request: Google asks you to authorize suSHi
  3. Callback: Google redirects back with authorization code
  4. Token Exchange: suSHi exchanges code for access token
  5. Fetch Profile: suSHi retrieves your email and name
  6. Account Creation: Your account is created or updated

Requested Scopes

suSHi requests the following permissions:
scopes: [
  "https://www.googleapis.com/auth/userinfo.email",
  "https://www.googleapis.com/auth/userinfo.profile"
]
Why these scopes?
  • userinfo.email: Required to identify your account
  • userinfo.profile: Used to display your name in the dashboard
suSHi only requests read-only access to basic profile information. No access to Gmail, Drive, or other Google services.

User Information Retrieved

From Google’s API:
{
  "email": "[email protected]",
  "name": "John Doe"
}
This information is used to:
  • Create your suSHi account (email as username)
  • Display your name in the dashboard
  • Associate machines with your account

Authorization URL

Generated URL format:
https://accounts.google.com/o/oauth2/auth?
  client_id=YOUR_CLIENT_ID&
  redirect_uri=https://your-domain.com/auth/google/callback&
  response_type=code&
  scope=email+profile&
  state=RANDOM_STATE_STRING
  • client_id: Your Google OAuth app identifier
  • redirect_uri: Where to send users after authorization
  • response_type: code for authorization code flow
  • scope: Requested permissions (space-separated)
  • state: Random string to prevent CSRF attacks

GitHub OAuth

Authorization Flow

When you sign in with GitHub:
  1. Redirect to GitHub: You’re sent to GitHub’s OAuth authorization page
  2. Permission Request: GitHub asks you to authorize suSHi
  3. Callback: GitHub redirects back with authorization code
  4. Token Exchange: suSHi exchanges code for access token
  5. Fetch Profile: suSHi retrieves your email and username
  6. Email Verification: If email not public, fetches from emails API
  7. Account Creation: Your account is created or updated

Requested Scopes

suSHi requests:
scopes: ["read:user", "read:email"]
Why these scopes?
  • read:user: Grants read-only access to profile information
  • read:email: Needed to access your verified email addresses
  • GitHub doesn’t include email in basic profile by default
  • suSHi needs your email to create your account
If your email is public on GitHub, we fetch it from your profile. Otherwise, we use the verified primary email from your account settings.

User Information Retrieved

From GitHub’s API:
// Primary API call: GET /user
{
  "name": "John Doe",
  "email": "[email protected]"  // If public
}

// Fallback API call: GET /user/emails
[
  {
    "email": "[email protected]",
    "primary": true,
    "verified": true
  }
]
Email Selection Priority:
  1. Email from main profile (if public)
  2. Primary verified email from emails API
  3. First verified email found
If no verified email is found on your GitHub account, authentication will fail. Verify at least one email address in GitHub settings.

Authorization URL

Generated URL format:
https://github.com/login/oauth/authorize?
  client_id=YOUR_CLIENT_ID&
  redirect_uri=https://your-domain.com/auth/github/callback&
  scope=user:email&
  state=RANDOM_STATE_STRING

Security Features

The state parameter prevents CSRF attacks:
  1. suSHi generates a random state string
  2. Stores it in your session
  3. Sends it to OAuth provider
  4. Validates it on callback
If the state doesn’t match, the request is rejected.
// State verification on callback
if receivedState != storedState {
    return error("Invalid state parameter")
}

Setting Up OAuth (For Self-Hosting)

If you’re running your own suSHi instance, you’ll need to configure OAuth:

Google OAuth Setup

1

Create Project

2

Enable OAuth

Navigate to APIs & Services > Credentials
3

Create OAuth Client

Click Create Credentials > OAuth 2.0 Client ID
4

Configure Client

  • Application type: Web application
  • Authorized redirect URIs: https://your-domain.com/auth/google/callback
5

Get Credentials

Copy the Client ID and Client Secret
6

Update Config

Add to your suSHi configuration:
oauth:
  google:
    client_id: "YOUR_CLIENT_ID"
    client_secret: "YOUR_CLIENT_SECRET"
    redirect_url: "https://your-domain.com/auth/google/callback"
    scopes:
      - "https://www.googleapis.com/auth/userinfo.email"
      - "https://www.googleapis.com/auth/userinfo.profile"
    state: "RANDOM_STRING"  # Generate securely

GitHub OAuth Setup

1

Developer Settings

2

New OAuth App

Click New OAuth App
3

Configure App

  • Application name: suSHi
  • Homepage URL: https://your-domain.com
  • Callback URL: https://your-domain.com/auth/github/callback
4

Get Credentials

Copy the Client ID and generate a Client Secret
5

Update Config

Add to your suSHi configuration:
oauth:
  github:
    client_id: "YOUR_CLIENT_ID"
    client_secret: "YOUR_CLIENT_SECRET"
    redirect_url: "https://your-domain.com/auth/github/callback"
    scopes:
      - "user:email"
    state: "RANDOM_STRING"  # Generate securely
The state parameter should be a cryptographically random string. Generate it securely and keep it secret.

Using OAuth in Your Application

Initiating OAuth Flow

To start authentication:
<!-- Google Sign-In Button -->
<a href="/auth/google">
  <button>Sign in with Google</button>
</a>

<!-- GitHub Sign-In Button -->
<a href="/auth/github">
  <button>Sign in with GitHub</button>
</a>
Backend generates the authorization URL:
// Google
func GenerateGoogleAuthURL(config models.Config) string {
    params := url.Values{}
    params.Add("client_id", config.OAuthConfig.Google.ClientID)
    params.Add("redirect_uri", config.OAuthConfig.Google.RedirectURL)
    params.Add("response_type", "code")
    params.Add("scope", strings.Join(config.OAuthConfig.Google.Scopes, " "))
    params.Add("state", config.OAuthConfig.Google.State)
    
    return "https://accounts.google.com/o/oauth2/auth?" + params.Encode()
}

Handling Callback

After user authorizes:
// Extract authorization code
code := r.URL.Query().Get("code")
state := r.URL.Query().Get("state")

// Validate state (CSRF protection)
if state != expectedState {
    return error("Invalid state")
}

// Exchange code for token
email, name, err := HandleGoogleCallback(config, code)

// Create or update user
user := CreateOrUpdateUser(email, name)

// Issue JWT token
jwtToken := GenerateJWT(user)

// Send token to client
return jwtToken

Troubleshooting

Error: redirect_uri_mismatch or The redirect URI in the request does not matchCause: The redirect URI in your request doesn’t match the one configured in OAuth app settings.Solutions:
  1. Verify exact match (including protocol, domain, path)
  2. Check for trailing slashes: /callback vs /callback/
  3. Ensure HTTPS in production
  4. Update OAuth app settings to match your configuration
Error: Invalid state parameter or CSRF protection failedCause: State parameter doesn’t match between request and callbackSolutions:
  1. Ensure state is stored in session/cookie
  2. Check for session expiry
  3. Verify state generation and validation logic
  4. Clear browser cookies and retry
Error: No verified email found on GitHub accountCause: GitHub account has no verified email addressesSolutions:
  1. Go to GitHub Email Settings
  2. Add and verify an email address
  3. Make email public or ensure it’s verified
  4. Retry authentication
Error: Failed to exchange code for tokenCause: Authorization code expired or invalid credentialsSolutions:
  1. Code expires quickly - complete flow faster
  2. Verify client ID and secret are correct
  3. Check OAuth app is not suspended
  4. Ensure server time is accurate (for token validation)

Best Practices

Use Environment Variables

Never hardcode OAuth credentials. Use environment variables or secret management.
export GOOGLE_CLIENT_ID="..."
export GOOGLE_CLIENT_SECRET="..."

Rotate Secrets Regularly

Periodically regenerate OAuth client secrets for security.

Monitor OAuth Usage

Track authentication failures and suspicious patterns in logs.

Implement Rate Limiting

Prevent brute force attacks on OAuth endpoints with rate limiting.

Next Steps

Security

Learn about security measures in suSHi

Machine Management

Start adding your SSH machines

Build docs developers (and LLMs) love