GitHub Authentication
Enable GitHub OAuth login with a ready-made wrapper that configures PKCE, token persistence, and session cookie forwarding.
See also: Provider setup checklist in the OAuth 2.0 guide.
Full source
File: pwsh/tutorial/examples/8.10-GitHubAuthentication.ps1
<#
Sample: GitHub Authentication (Authorization Code) + Cookies
Purpose: Ready-to-use GitHub OAuth2 login using the convenience function.
Based on: 8.10-GitHubAuthentication.ps1, simplified and focused on GitHub.
Notes:
- This registers three schemes when using Add-KrGitHubAuthentication -AuthenticationScheme 'GitHub':
1. 'GitHub' → OAuth challenge (remote login) scheme
- PKCE is enabled and tokens are saved to the cookie session.
- Email claim is optionally enriched from /user/emails when permitted by scope and consent.
- Set environment variables first:
$env:GITHUB_CLIENT_ID = 'your-client-id'
$env:GITHUB_CLIENT_SECRET = 'your-client-secret'
- Do NOT hardcode real secrets in sample code.
#>
param(
[int]$Port = 5000,
[IPAddress]$IPAddress = [IPAddress]::Loopback,
[string]$ClientId = $env:GITHUB_CLIENT_ID,
[string]$ClientSecret = $env:GITHUB_CLIENT_SECRET,
[string]$CallbackPath = '/signin-oauth'
)
if (([string]::IsNullOrWhiteSpace( $ClientId)) -and
([string]::IsNullOrWhiteSpace( $ClientSecret)) -and
(Test-Path -Path .\Utility\Import-EnvFile.ps1)) {
& .\Utility\Import-EnvFile.ps1
$ClientId = $env:GITHUB_CLIENT_ID
$ClientSecret = $env:GITHUB_CLIENT_SECRET
}
if (-not $ClientId -or -not $ClientSecret) {
Write-Error 'ERROR: Set GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET environment variables before running.'
exit 1
}
# This is recommended in order to use relative paths without issues
Initialize-KrRoot -Path $PSScriptRoot
# 1) Logging
New-KrLogger |
Set-KrLoggerLevel -Value Debug |
Add-KrSinkConsole |
Register-KrLogger -Name 'console' -SetAsDefault | Out-Null
# 2) Server
New-KrServer -Name 'GitHub Authentication Demo'
# 3) HTTPS listener (self-signed)
Add-KrEndpoint -Port $Port -IPAddress $IPAddress -SelfSignedCert
# 4) GitHub auth (adds 'GitHub', 'GitHub.Cookies', 'GitHub.Policy')
# Customize callback if your GitHub App uses a different path (e.g. '/signin-oauth').
# To disable email enrichment: add -DisableEmailEnrichment
Add-KrGitHubAuthentication -AuthenticationScheme 'GitHub' -ClientId $ClientId -ClientSecret $ClientSecret -CallbackPath $CallbackPath
# 5) Finalize configuration
Enable-KrConfiguration
# 6) Landing page
Add-KrHtmlTemplateRoute -Pattern '/' -HtmlTemplatePath './Assets/wwwroot/github/github-auth.html'
# 7) Protected routes using the policy scheme
Add-KrMapRoute -Verbs Get -Pattern '/github/login' -AuthorizationScheme 'GitHub' -ScriptBlock {
# Extract user claims (robust to GitHub-specific claim types)
$name = $Context.User.Identity.Name ?? '(no name)'
$email = $Context.User.FindFirst([System.Security.Claims.ClaimTypes]::Email)?.Value `
?? $Context.User.FindFirst('email')?.Value `
?? 'No email claim'
$username = $Context.User.FindFirst('urn:github:login')?.Value `
?? $Context.User.FindFirst('login')?.Value `
?? $Context.User.FindFirst('preferred_username')?.Value `
?? $name
if ([string]::IsNullOrWhiteSpace($username)) { $username = 'N/A' }
$githubId = $Context.User.FindFirst([System.Security.Claims.ClaimTypes]::NameIdentifier)?.Value `
?? $Context.User.FindFirst('id')?.Value `
?? $Context.User.FindFirst('sub')?.Value `
?? 'N/A'
$profileUrl = $Context.User.FindFirst('urn:github:html_url')?.Value `
?? $Context.User.FindFirst('profile')?.Value `
?? $Context.User.FindFirst('url')?.Value `
?? "https://github.com/$username"
# Get authentication details
$authType = $Context.User.Identity.AuthenticationType ?? 'Unknown'
$isAuthenticated = $Context.User.Identity.IsAuthenticated
Write-KrHtmlResponse -FilePath './Assets/wwwroot/github/protected.html' -Variables @{
name = $name
email = $email
username = $username
githubId = $githubId
profileUrl = $profileUrl
authType = $authType
isAuthenticated = $isAuthenticated
}
}
Add-KrMapRoute -Verbs Get -Pattern '/github/me' -AuthorizationScheme 'GitHub' -ScriptBlock {
$claims = foreach ($c in $Context.User.Claims) { @{ Type = $c.Type; Value = $c.Value } }
Write-KrJsonResponse @{ scheme = 'GitHub'; authenticated = $Context.User.Identity.IsAuthenticated; claims = $claims }
}
Add-KrMapRoute -Verbs Get -Pattern '/github/logout' -ScriptBlock {
Invoke-KrCookieSignOut -AuthKind 'OAuth2' -Scheme 'GitHub' -RedirectUri '/'
# Write-KrTextResponse 'Signed out (local cookie cleared).'
}
# 8) Start
Start-KrServer -CloseLogsOnExit
Step-by-step
- Logger: Register a console logger at Debug so auth and pipeline events are visible.
- Server: Create a server named ‘GitHub Authentication Demo’.
- Listener: Bind a self-signed HTTPS endpoint on loopback and chosen port (5000 default).
- Auth: Call
Add-KrGitHubAuthentication– registers base, cookie, and policy schemes plus PKCE and scope defaults. Default callback is/signin-oauth; override with-CallbackPathto match your GitHub App. - Configuration: Finalize middleware with
Enable-KrConfiguration. - Routes: Public landing + protected routes using
-AuthorizationScheme 'GitHub'(policy scheme forwards challenge to GitHub and authenticates via cookie). - Email enrichment: Automatically fetches primary/verified email if absent and scope
user:emailgranted. - Start: Launch the server with
Start-KrServer.
Try it
# Set secrets first (replace placeholders)
$env:GITHUB_CLIENT_ID = 'your-client-id'
$env:GITHUB_CLIENT_SECRET = 'your-client-secret'
# Start script (from repo root)
pwsh docs/_includes/examples/pwsh/8.10-GitHubAuthentication.ps1
# After browser login, inspect claims via API
Invoke-WebRequest https://localhost:5000/github/me -SkipCertificateCheck | Select -Expand Content
Key Points
- Three schemes:
GitHub,GitHub.Cookies,GitHub.Policy(use-AuthorizationScheme 'GitHub'on protected routes). - PKCE is on by default for the authorization code flow.
- Default scopes include
read:useranduser:email; add more with-Scope. - Email claim added only if missing and consented (
user:emailscope); disable via-DisableEmailEnrichment. - Cookie scheme isolates session persistence from remote challenge logic.
- The GitHub OAuth App callback must match
https://localhost:<port><CallbackPath>. Default path is/signin-oauth(as used in the sample). Change with-CallbackPathif your app differs.
Troubleshooting
| Symptom | Cause | Resolution |
|---|---|---|
| No email claim | Scope not granted or email private | Include user:email scope and ensure account has a primary email |
| 410 Gone on userinfo | Wrong endpoint or missing User-Agent | Wrapper uses https://api.github.com/user with UA; check network |
| Infinite redirect loop | Callback mismatch | Set callback to https://localhost:5011/signin-github in GitHub App settings |
| Avatar claim missing | Field absent or mapping overridden | Ensure wrapper not replaced; verify /user JSON |
References
- Add-KrGitHubAuthentication
- Add-KrOAuth2Authentication
- Add-KrMapRoute
- Add-KrRouteGroup
- New-KrServer
- Add-KrEndpoint
- Enable-KrConfiguration
- Start-KrServer
- Register-KrLogger
- Set-KrLoggerLevel
- Add-KrSinkConsole
Previous / Next
- Previous: Multiple Schemes
- Next: OpenID Connect (Duende Demo)