Skip to content

Commit

Permalink
Added device flow login in addition to PAT
Browse files Browse the repository at this point in the history
  • Loading branch information
MariusStorhaug committed Sep 19, 2023
1 parent d9c7d7f commit cb7a3c0
Show file tree
Hide file tree
Showing 7 changed files with 226 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .dev/localImport.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
$repoPath = (Split-Path $PSScriptRoot -Parent)
$modulePath = Join-Path $repoPath 'src' 'GitHub'

$PSModulePath += ";$modulePath"
Import-Module "$modulePath"
2 changes: 1 addition & 1 deletion scripts/GitHubAPI.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
$APIDocURI = 'https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json'
$Response = Invoke-WebRequest -Uri $APIDocURI -Method Get -UseBasicParsing
$Response = Invoke-RestMethod -Uri $APIDocURI -Method Get
$APIDoc = $Response.Content | ConvertFrom-Json
$APIDoc.tags
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
function Invoke-GitHubDeviceCodeLogin {
<#
.SYNOPSIS
Starts the GitHub Device Flow login process.
.DESCRIPTION
Starts the GitHub Device Flow login process. This will prompt the user to visit a URL and enter a code.
.EXAMPLE
Invoke-GitHubDeviceCodeLogin
This will start the GitHub Device Flow login process.
The user gets prompted to visit a URL and enter a code.
.NOTES
For more info about the Device Flow visit:
https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/building-a-cli-with-a-github-app
#>
[OutputType([void])]
[CmdletBinding()]
param(
# The Client ID of the GitHub App.
[Parameter()]
[string] $ClientID = 'Iv1.f26b61bc99e69405'
)

$deviceCodeResponse = Request-GitHubDeviceCode -ClientID $ClientID

$deviceCode = $deviceCodeResponse.device_code
$interval = $deviceCodeResponse.interval
$userCode = $deviceCodeResponse.user_code
$verificationUri = $deviceCodeResponse.verification_uri

Write-Host "Please visit: $verificationUri"
Write-Host "and enter code: $userCode"

$token = Wait-GitHubToken -DeviceCode $deviceCode -ClientID $ClientID -Interval $interval

Write-Host 'Successfully authenticated!'
$token
}
41 changes: 41 additions & 0 deletions src/GitHub/private/Auth/DeviceFlow/Request-GitHubDeviceCode.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
function Request-GitHubDeviceCode {
<#
.SYNOPSIS
Request a GitHub Device Code.
.DESCRIPTION
Request a GitHub Device Code.
.EXAMPLE
Request-GitHubDeviceCode -ClientID $ClientID
This will request a GitHub Device Code.
.NOTES
For more info about the Device Flow visit:
https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/building-a-cli-with-a-github-app
#>
[OutputType([PSCustomObject])]
[CmdletBinding()]
param(
# The Client ID of the GitHub App.
[Parameter()]
[string] $ClientID
)
$RESTParams = @{
Uri = 'https://github.com/login/device/code'
Method = 'POST'
Body = @{ 'client_id' = $ClientID }
Headers = @{ 'Accept' = 'application/json' }
}
try {
Write-Verbose ($RESTParams.GetEnumerator() | Out-String)

$deviceCodeResponse = Invoke-RestMethod @RESTParams -Verbose:$false
return $deviceCodeResponse
} catch {
Write-Error $_.Exception.Message
throw $_
}
}

55 changes: 55 additions & 0 deletions src/GitHub/private/Auth/DeviceFlow/Request-GitHubToken.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
function Request-GitHubToken {
<#
.SYNOPSIS
Request a GitHub token using the Device Flow.
.DESCRIPTION
Request a GitHub token using the Device Flow.
This will poll the GitHub API until the user has entered the code.
.EXAMPLE
Request-GitHubToken -DeviceCode $deviceCode -ClientID $ClientID
This will poll the GitHub API until the user has entered the code.
.NOTES
For more info about the Device Flow visit:
https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/building-a-cli-with-a-github-app
#>
[OutputType([PSCustomObject])]
[CmdletBinding()]
param(
# The `device_code` used to request the access token.
[Parameter(Mandatory)]
[string] $DeviceCode,

# The Client ID of the GitHub App.
[Parameter()]
[string] $ClientID,

# The refresh token used create a new access token.
[Parameter()]
[string] $RefreshToken
)

$RESTParams = @{
Uri = 'https://github.com/login/oauth/access_token'
Method = 'POST'
Body = @{
'client_id' = $ClientID
'device_code' = $DeviceCode
'grant_type' = 'urn:ietf:params:oauth:grant-type:device_code'
}
Headers = @{ 'Accept' = 'application/json' }
}

try {
Write-Verbose ($RESTParams.GetEnumerator() | Out-String)

$tokenResponse = Invoke-RestMethod @RESTParams -Verbose:$false
return $tokenResponse
} catch {
Write-Error $_.Exception.Message
throw $_
}
}
76 changes: 76 additions & 0 deletions src/GitHub/private/Auth/DeviceFlow/Wait-GitHubToken.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@

function Wait-GitHubToken {
<#
.SYNOPSIS
Waits for the GitHub Device Flow to complete.
.DESCRIPTION
Waits for the GitHub Device Flow to complete.
This will poll the GitHub API until the user has entered the code.
.EXAMPLE
Wait-GitHubToken -DeviceCode $deviceCode -ClientID $ClientID -Interval $interval
This will poll the GitHub API until the user has entered the code.
.NOTES
For more info about the Device Flow visit:
https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/building-a-cli-with-a-github-app
#>
[OutputType([PSCustomObject])]
[CmdletBinding()]
param(
# The `device_code` used to request the token.
[Parameter(Mandatory)]
[string] $DeviceCode,

# The Client ID of the GitHub App.
[Parameter()]
[string] $ClientID,

# The interval to wait between polling for the token.
[Parameter()]
[int] $Interval = 5
)

do {
$response = Request-GitHubToken -DeviceCode $DeviceCode -ClientID $ClientID
if ($response.error) {
switch ($response.error) {
'authorization_pending' {
# The user has not yet entered the code.
# Wait, then poll again.
Write-Verbose $response.error_description
Start-Sleep -Seconds $interval
continue
}
'slow_down' {
# The app polled too fast.
# Wait for the interval plus 5 seconds, then poll again.
Write-Verbose $response.error_description
Start-Sleep -Seconds ($interval + 5)
continue
}
'expired_token' {
# The `device_code` expired, and the process needs to restart.
Write-Error $response.error_description
exit 1
}
'access_denied' {
# The user cancelled the process. Stop polling.
Write-Error $response.error_description
exit 1
}
default {
# The response contains an access token. Stop polling.
Write-Error 'Unknown error:'
Write-Error $response.error
Write-Error $response.error_description
Write-Error $response.error_uri
break
}
}
}
} until ($response.access_token)
$response
}
8 changes: 7 additions & 1 deletion src/GitHub/public/Core/Connect-GitHubAccount.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
[Parameter()]
[String] $Repo,

[Parameter(Mandatory)]
[Parameter()]
[String] $Token,

[Parameter()]
Expand All @@ -21,6 +21,12 @@
[string] $Version = '2022-11-28'
)

if ($Token) {
$script:Token = $Token
} else {
$script:Token = Invoke-GitHubDeviceCodeLogin
}

$script:APIBaseURI = $APIBaseURI
$script:Owner = $Owner
$script:Repo = $Repo
Expand Down

0 comments on commit cb7a3c0

Please sign in to comment.