Skip to main content
Retrieve GitHub user profiles and check OAuth permissions for the authenticated user.

GET /api/user-profile

Fetch a GitHub user’s public profile information.
username
string
required
GitHub username to fetch

Response

login
string
GitHub username
name
string
User’s display name
avatar_url
string
URL to user’s avatar image
html_url
string
URL to user’s GitHub profile
bio
string
User’s bio/description
company
string
User’s company
location
string
User’s location
blog
string
User’s blog/website URL
twitter_username
string
User’s Twitter/X username
followers
number
Number of followers
following
number
Number of users following
public_repos
number
Number of public repositories
type
string
User type (“User” or “Organization”)
created_at
string
Account creation timestamp (ISO 8601)
const username = 'octocat';
const response = await fetch(`/api/user-profile?username=${username}`);
const profile = await response.json();

console.log(profile);
/*
{
  login: "octocat",
  name: "The Octocat",
  avatar_url: "https://avatars.githubusercontent.com/u/583231",
  html_url: "https://github.com/octocat",
  bio: "GitHub mascot",
  company: "@github",
  location: "San Francisco",
  blog: "https://github.blog",
  twitter_username: "github",
  followers: 9876,
  following: 12,
  public_repos: 8,
  type: "User",
  created_at: "2011-01-25T18:44:36Z"
}
*/

Cache Control

The user profile endpoint returns private cache headers to prevent authenticated responses from being shared:
Cache-Control: private, no-store
This ensures that:
  • Responses are not cached by shared/public caches
  • Each user gets their own authenticated view
  • No sensitive data leaks between users

GET /api/user-scopes

Get OAuth scopes granted for the authenticated user’s GitHub account.

Response

scopes
array
Array of granted OAuth scope strings
const response = await fetch('/api/user-scopes');
const data = await response.json();

console.log(data.scopes);
/*
[
  "repo",
  "user",
  "read:org",
  "workflow"
]
*/

Common GitHub OAuth Scopes

Repository Scopes

  • repo - Full control of private repositories
  • repo:status - Access commit status
  • repo_deployment - Access deployment status
  • public_repo - Access public repositories
  • repo:invite - Access repository invitations
  • security_events - Read and write security events

User Scopes

  • user - Full user profile access (read/write)
  • read:user - Read user profile data
  • user:email - Access user email addresses
  • user:follow - Follow/unfollow users

Organization Scopes

  • admin:org - Full organization control
  • write:org - Write organization data
  • read:org - Read organization data

Other Scopes

  • gist - Create gists
  • notifications - Access notifications
  • workflow - Update GitHub Actions workflows
  • delete_repo - Delete repositories

Usage Examples

Display User Card

async function renderUserCard(username: string) {
  try {
    const profile = await fetch(`/api/user-profile?username=${username}`)
      .then(r => r.json());
    
    return `
      <div class="user-card">
        <img src="${profile.avatar_url}" alt="${profile.name}" />
        <h2>${profile.name || profile.login}</h2>
        <p class="username">@${profile.login}</p>
        ${profile.bio ? `<p class="bio">${profile.bio}</p>` : ''}
        <div class="stats">
          <span>${profile.followers} followers</span>
          <span>${profile.public_repos} repos</span>
        </div>
        ${profile.company ? `<p>📍 ${profile.company}</p>` : ''}
        ${profile.location ? `<p>🌍 ${profile.location}</p>` : ''}
      </div>
    `;
  } catch (error) {
    console.error('Failed to fetch user:', error);
    return '<p>User not found</p>';
  }
}

Compare User Stats

async function compareUsers(username1: string, username2: string) {
  const [user1, user2] = await Promise.all([
    fetch(`/api/user-profile?username=${username1}`).then(r => r.json()),
    fetch(`/api/user-profile?username=${username2}`).then(r => r.json())
  ]);
  
  return {
    followers: {
      [username1]: user1.followers,
      [username2]: user2.followers,
      leader: user1.followers > user2.followers ? username1 : username2
    },
    repos: {
      [username1]: user1.public_repos,
      [username2]: user2.public_repos,
      leader: user1.public_repos > user2.public_repos ? username1 : username2
    }
  };
}

Permission Gate

async function requireScopes(requiredScopes: string[]) {
  const { scopes } = await fetch('/api/user-scopes').then(r => r.json());
  
  const missingScopes = requiredScopes.filter(scope => !scopes.includes(scope));
  
  if (missingScopes.length > 0) {
    throw new Error(
      `Missing required permissions: ${missingScopes.join(', ')}. ` +
      `Please re-authenticate with additional scopes.`
    );
  }
  
  return true;
}

// Usage
try {
  await requireScopes(['repo', 'workflow']);
  // Proceed with GitHub Actions workflow updates
} catch (error) {
  console.error(error.message);
  // Show re-authentication prompt
}

Conditional UI Rendering

async function renderNavigation() {
  const { scopes } = await fetch('/api/user-scopes').then(r => r.json());
  
  const navItems = [
    { label: 'Repositories', path: '/repos', scope: 'repo' },
    { label: 'Pull Requests', path: '/pulls', scope: 'repo' },
    { label: 'Actions', path: '/actions', scope: 'workflow' },
    { label: 'Organizations', path: '/orgs', scope: 'read:org' },
    { label: 'Gists', path: '/gists', scope: 'gist' }
  ];
  
  return navItems
    .filter(item => !item.scope || scopes.includes(item.scope))
    .map(item => `<a href="${item.path}">${item.label}</a>`);
}