Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 42 additions & 21 deletions ServiceNow/Private/Get-ServiceNowAuth.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -38,31 +38,52 @@ function Get-ServiceNowAuth {
if ($ServiceNowSession.Version) { $hashOut.uri = $hashOut.uri + $ServiceNowSession.Version }

# check if we need a new access token
if ( $ServiceNowSession.ExpiresOn -lt (Get-Date) -and $ServiceNowSession.RefreshToken -and $ServiceNowSession.ClientCredential ) {
# we've expired and have a refresh token
$refreshParams = @{
Uri = 'https://{0}/oauth_token.do' -f $ServiceNowSession.Domain
Method = 'POST'
ContentType = 'application/x-www-form-urlencoded'
Body = @{
grant_type = 'refresh_token'
client_id = $ServiceNowSession.ClientCredential.UserName
client_secret = $ServiceNowSession.ClientCredential.GetNetworkCredential().password
refresh_token = $ServiceNowSession.RefreshToken.GetNetworkCredential().password
}
if ( $ServiceNowSession.ExpiresOn -lt (Get-Date) -and $ServiceNowSession.ClientCredential ) {

# Build refresh/re-auth body based on grant type
$refreshBody = @{
client_id = $ServiceNowSession.ClientCredential.UserName
client_secret = $ServiceNowSession.ClientCredential.GetNetworkCredential().password
}

$response = Invoke-RestMethod @refreshParams

$ServiceNowSession.AccessToken = New-Object System.Management.Automation.PSCredential('AccessToken', ($response.access_token | ConvertTo-SecureString -AsPlainText -Force))
$ServiceNowSession.RefreshToken = New-Object System.Management.Automation.PSCredential('RefreshToken', ($response.refresh_token | ConvertTo-SecureString -AsPlainText -Force))
if ($response.expires_in) {
$ServiceNowSession.ExpiresOn = (Get-Date).AddSeconds($response.expires_in)
Write-Verbose ('Access token has been refreshed and will expire at {0}' -f $ServiceNowSession.ExpiresOn)
if ($ServiceNowSession.GrantType -eq 'client_credentials') {
# Client credentials: re-authenticate (no refresh token available)
$refreshBody['grant_type'] = 'client_credentials'
}
elseif ($ServiceNowSession.RefreshToken) {
# Password grant: use refresh token
$refreshBody['grant_type'] = 'refresh_token'
$refreshBody['refresh_token'] = $ServiceNowSession.RefreshToken.GetNetworkCredential().password
}
else {
Write-Warning 'Access token expired but no refresh method available'
}

if ($refreshBody.ContainsKey('grant_type')) {
$refreshParams = @{
Uri = 'https://{0}/oauth_token.do' -f $ServiceNowSession.Domain
Method = 'POST'
ContentType = 'application/x-www-form-urlencoded'
Body = $refreshBody
}

# ensure script/module scoped variable is updated
$script:ServiceNowSession = $ServiceNowSession
$response = Invoke-RestMethod @refreshParams

$ServiceNowSession.AccessToken = New-Object System.Management.Automation.PSCredential('AccessToken', ($response.access_token | ConvertTo-SecureString -AsPlainText -Force))

# Update refresh token if provided (password grant only)
if ($response.refresh_token) {
$ServiceNowSession.RefreshToken = New-Object System.Management.Automation.PSCredential('RefreshToken', ($response.refresh_token | ConvertTo-SecureString -AsPlainText -Force))
}

if ($response.expires_in) {
$ServiceNowSession.ExpiresOn = (Get-Date).AddSeconds($response.expires_in)
Write-Verbose ('Access token has been refreshed and will expire at {0}' -f $ServiceNowSession.ExpiresOn)
}

# ensure script/module scoped variable is updated
$script:ServiceNowSession = $ServiceNowSession
}
}

if ( $ServiceNowSession.AccessToken ) {
Expand Down
44 changes: 32 additions & 12 deletions ServiceNow/Public/New-ServiceNowSession.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ function New-ServiceNowSession {

[Parameter(Mandatory, ParameterSetName = 'OAuth')]
[Parameter(Mandatory, ParameterSetName = 'OAuthProxy')]
[Parameter(Mandatory, ParameterSetName = 'OAuthClientCredential')]
Copy link
Collaborator

@gdbarron gdbarron Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we'll need to add a OAuthClientCredentialProxy paramset for ClientCredential, Proxy, and ProxyCredential params.

[System.Management.Automation.PSCredential] $ClientCredential,

[Parameter(Mandatory, ParameterSetName = 'AccessToken')]
Expand Down Expand Up @@ -160,19 +161,32 @@ function New-ServiceNowSession {
}

$script:PSDefaultParameterValues['Invoke-WebRequest:TimeoutSec'] = $TimeoutSec
$script:PSDefaultParameterValues['Invoke-RestMethodt:TimeoutSec'] = $TimeoutSec
$script:PSDefaultParameterValues['Invoke-RestMethod:TimeoutSec'] = $TimeoutSec

switch -Wildcard ($PSCmdLet.ParameterSetName) {
'OAuth*' {
# Determine OAuth grant type and build request body
$oauthBody = @{
'client_id' = $ClientCredential.UserName
'client_secret' = $ClientCredential.GetNetworkCredential().Password
}

if ($PSCmdLet.ParameterSetName -eq 'OAuthClientCredential') {
# Client Credentials Grant (machine-to-machine)
$oauthBody['grant_type'] = 'client_credentials'
$grantType = 'client_credentials'
}
else {
# Password Grant (user credentials)
$oauthBody['grant_type'] = 'password'
$oauthBody['username'] = $Credential.UserName
$oauthBody['password'] = $Credential.GetNetworkCredential().Password
$grantType = 'password'
}

$params = @{
Uri = 'https://{0}/oauth_token.do' -f $Url
Body = @{
'grant_type' = 'password'
'client_id' = $ClientCredential.UserName
'client_secret' = $ClientCredential.GetNetworkCredential().Password
'username' = $Credential.UserName
'password' = $Credential.GetNetworkCredential().Password
}
Body = $oauthBody
Method = 'Post'
UseBasicParsing = $true
}
Expand All @@ -198,18 +212,24 @@ function New-ServiceNowSession {
if ( $response.Content ) {
$token = $response.Content | ConvertFrom-Json
$newSession.Add('AccessToken', (New-Object System.Management.Automation.PSCredential('AccessToken', ($token.access_token | ConvertTo-SecureString -AsPlainText -Force))))
$newSession.Add('RefreshToken', (New-Object System.Management.Automation.PSCredential('RefreshToken', ($token.refresh_token | ConvertTo-SecureString -AsPlainText -Force))))

# Password grant returns refresh_token, client credentials does not
if ($token.refresh_token) {
$newSession.Add('RefreshToken', (New-Object System.Management.Automation.PSCredential('RefreshToken', ($token.refresh_token | ConvertTo-SecureString -AsPlainText -Force))))
}

if ($token.expires_in) {
$expiryTime = (Get-Date).AddSeconds($token.expires_in)
$newSession.Add('ExpiresOn', $expiryTime)
Write-Verbose "Access token will expire at $expiryTime"
}
# store client credential as it will be needed to refresh the access token
$newSession.Add('ClientCredential', $ClientCredential)

# Store credentials and grant type for token refresh
$newSession.Add('ClientCredential', $ClientCredential)
$newSession.Add('GrantType', $grantType)
} else {
# invoke-webrequest didn't throw an error, but we didn't get a token back either
throw ('"{0} : {1}' -f $response.StatusCode, $response | Out-String )
throw ('{0} : {1}' -f $response.StatusCode, $response | Out-String )
}
}

Expand Down