Following my earlier posts on obtaining, checking and renewing Bearer Tokens for the Jamf Pro API and the deprecation of Basic Authentication for the Jamf Pro Classic API, @bryson3gps reached out to let me know there was a simpler way to get the Bearer Token which didn’t require the prior encoding of the username and password credentials in base64 format.
The command shown below will handle obtaining the token using Basic Authentication on macOS Monterey and later:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
curl -X POST -u username:password -s https://server.name.here/api/v1/auth/token | plutil -extract token raw – |
The command shown below will handle obtaining the token using Basic Authentication on macOS Big Sur and earlier:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
curl -X POST -u username:password -s https://server.name.here/api/v1/auth/token | python -c 'import sys, json; print json.load(sys.stdin)["token"]' |
This allows the following functions to be collapsed into one command:
- Encoding the username and password in base64 format
- Obtaining a Bearer Token using Basic Authentication
- Storing the Bearer Token (if command is used in a variable.)
He also pointed out that I was using an incorrect API call for the validation check which uses HTTP status codes. What I had:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/usr/bin/curl –write-out %{http_code} –silent –output /dev/null "$jamfpro_url/api/v1/auth/keep-alive" –request POST –header "Authorization: Bearer ${api_token}" |
While this worked, it was using the keepalive endpoint with a POST request, which is used to invalidate tokens and issue new ones. I’ve updated to use this instead:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/usr/bin/curl –write-out %{http_code} –silent –output /dev/null "$jamfpro_url/api/v1/auth" –request GET –header "Authorization: Bearer ${api_token}" |
This API call sends a GET request to the auth endpoint, which returns all the authorization details associated with the current Bearer Token. This will work for the validation check and won’t trigger accidental invalidation of the existing Bearer Token.
With this in mind, the process of obtaining Bearer Tokens is now simplified. This affects the deprecation of the Classic API for Jamf Pro 10.35.0 and later by changing the workflow from this:
To this:
I’ve incorporated these changes into an updated script with functions for obtaining, checking and renewing Bearer Tokens for the Classic (for Jamf Pro 10.35.0 and later) and Jamf Pro APIs. For more details, please see below the jump.
Please see below for the updated script:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# This script uses the Jamf Pro API to get an authentication token | |
# Set default exit code | |
exitCode=0 | |
# Explicitly set initial value for the api_token variable to null: | |
api_token="" | |
# Explicitly set initial value for the token_expiration variable to null: | |
token_expiration="" | |
# If you choose to hardcode API information into the script, set one or more of the following values: | |
# | |
# The username for an account on the Jamf Pro server with sufficient API privileges | |
# The password for the account | |
# The Jamf Pro URL | |
# Set the Jamf Pro URL here if you want it hardcoded. | |
jamfpro_url="" | |
# Set the username here if you want it hardcoded. | |
jamfpro_user="" | |
# Set the password here if you want it hardcoded. | |
jamfpro_password="" | |
# Read the appropriate values from ~/Library/Preferences/com.github.jamfpro-info.plist | |
# if the file is available. To create the file, run the following commands: | |
# | |
# defaults write $HOME/Library/Preferences/com.github.jamfpro-info jamfpro_url https://jamf.pro.server.here | |
# defaults write $HOME/Library/Preferences/com.github.jamfpro-info jamfpro_user API_account_username_goes_here | |
# defaults write $HOME/Library/Preferences/com.github.jamfpro-info jamfpro_password API_account_password_goes_here | |
# | |
if [[ -f "$HOME/Library/Preferences/com.github.jamfpro-info.plist" ]]; then | |
if [[ -z "$jamfpro_url" ]]; then | |
jamfpro_url=$(defaults read $HOME/Library/Preferences/com.github.jamfpro-info jamfpro_url) | |
fi | |
if [[ -z "$jamfpro_user" ]]; then | |
jamfpro_user=$(defaults read $HOME/Library/Preferences/com.github.jamfpro-info jamfpro_user) | |
fi | |
if [[ -z "$jamfpro_password" ]]; then | |
jamfpro_password=$(defaults read $HOME/Library/Preferences/com.github.jamfpro-info jamfpro_password) | |
fi | |
fi | |
# If the Jamf Pro URL, the account username or the account password aren't available | |
# otherwise, you will be prompted to enter the requested URL or account credentials. | |
if [[ -z "$jamfpro_url" ]]; then | |
read -p "Please enter your Jamf Pro server URL : " jamfpro_url | |
fi | |
if [[ -z "$jamfpro_user" ]]; then | |
read -p "Please enter your Jamf Pro user account : " jamfpro_user | |
fi | |
if [[ -z "$jamfpro_password" ]]; then | |
read -p "Please enter the password for the $jamfpro_user account: " -s jamfpro_password | |
fi | |
echo | |
# Remove the trailing slash from the Jamf Pro URL if needed. | |
jamfpro_url=${jamfpro_url%%/} | |
GetJamfProAPIToken() { | |
# This function uses Basic Authentication to get a new bearer token for API authentication. | |
# Use user account's username and password credentials with Basic Authorization to request a bearer token. | |
if [[ $(/usr/bin/sw_vers -productVersion | awk -F . '{print $1}') -lt 12 ]]; then | |
api_token=$(/usr/bin/curl -X POST –silent -u "${jamfpro_user}:${jamfpro_password}" "$jamfpro_url/api/v1/auth/token" | python -c 'import sys, json; print json.load(sys.stdin)["token"]') | |
else | |
api_token=$(/usr/bin/curl -X POST –silent -u "${jamfpro_user}:${jamfpro_password}" "$jamfpro_url/api/v1/auth/token" | plutil -extract token raw –) | |
fi | |
} | |
APITokenValidCheck() { | |
# Verify that API authentication is using a valid token by running an API command | |
# which displays the authorization details associated with the current API user. | |
# The API call will only return the HTTP status code. | |
api_authentication_check=$(/usr/bin/curl –write-out %{http_code} –silent –output /dev/null "$jamfpro_url/api/v1/auth" –request GET –header "Authorization: Bearer ${api_token}") | |
} | |
CheckAndRenewAPIToken() { | |
# Verify that API authentication is using a valid token by running an API command | |
# which displays the authorization details associated with the current API user. | |
# The API call will only return the HTTP status code. | |
APITokenValidCheck | |
# If the api_authentication_check has a value of 200, that means that the current | |
# bearer token is valid and can be used to authenticate an API call. | |
if [[ ${api_authentication_check} == 200 ]]; then | |
# If the current bearer token is valid, it is used to connect to the keep-alive endpoint. This will | |
# trigger the issuing of a new bearer token and the invalidation of the previous one. | |
if [[ $(/usr/bin/sw_vers -productVersion | awk -F . '{print $1}') -lt 12 ]]; then | |
api_token=$(/usr/bin/curl "$jamfpro_url/api/v1/auth/keep-alive" –silent –request POST –header "Authorization: Bearer ${api_token}" | python -c 'import sys, json; print json.load(sys.stdin)["token"]') | |
else | |
api_token=$(/usr/bin/curl "$jamfpro_url/api/v1/auth/keep-alive" –silent –request POST –header "Authorization: Bearer ${api_token}" | plutil -extract token raw –) | |
fi | |
else | |
# If the current bearer token is not valid, this will trigger the issuing of a new bearer token | |
# using Basic Authentication. | |
GetJamfProAPIToken | |
fi | |
} | |
InvalidateToken() { | |
# Verify that API authentication is using a valid token by running an API command | |
# which displays the authorization details associated with the current API user. | |
# The API call will only return the HTTP status code. | |
APITokenValidCheck | |
# If the api_authentication_check has a value of 200, that means that the current | |
# bearer token is valid and can be used to authenticate an API call. | |
if [[ ${api_authentication_check} == 200 ]]; then | |
# If the current bearer token is valid, an API call is sent to invalidate the token. | |
authToken=$(/usr/bin/curl "$jamfpro_url/api/v1/auth/invalidate-token" –silent –header "Authorization: Bearer ${api_token}" -X POST) | |
# Explicitly set value for the api_token variable to null. | |
api_token="" | |
fi | |
} | |
GetJamfProAPIToken | |
APITokenValidCheck | |
echo "$api_authentication_check" | |
echo "$api_token" | |
CheckAndRenewAPIToken | |
APITokenValidCheck | |
echo "$api_authentication_check" | |
echo "$api_token" | |
InvalidateToken | |
APITokenValidCheck | |
echo "$api_authentication_check" | |
echo "$api_token" |
I’ve updated the original script with the corrected API validation check, but otherwise left it unaltered. That script is available below:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# This script uses the Jamf Pro API to get an authentication token | |
# Set default exit code | |
exitCode=0 | |
# Explicitly set initial value for the api_token variable to null: | |
api_token="" | |
# Explicitly set initial value for the token_expiration variable to null: | |
token_expiration="" | |
# If you choose to hardcode API information into the script, set one or more of the following values: | |
# | |
# The username for an account on the Jamf Pro server with sufficient API privileges | |
# The password for the account | |
# The Jamf Pro URL | |
# Set the Jamf Pro URL here if you want it hardcoded. | |
jamfpro_url="" | |
# Set the username here if you want it hardcoded. | |
jamfpro_user="" | |
# Set the password here if you want it hardcoded. | |
jamfpro_password="" | |
# Read the appropriate values from ~/Library/Preferences/com.github.jamfpro-info.plist | |
# if the file is available. To create the file, run the following commands: | |
# | |
# defaults write $HOME/Library/Preferences/com.github.jamfpro-info jamfpro_url https://jamf.pro.server.here | |
# defaults write $HOME/Library/Preferences/com.github.jamfpro-info jamfpro_user API_account_username_goes_here | |
# defaults write $HOME/Library/Preferences/com.github.jamfpro-info jamfpro_password API_account_password_goes_here | |
# | |
if [[ -f "$HOME/Library/Preferences/com.github.jamfpro-info.plist" ]]; then | |
if [[ -z "$jamfpro_url" ]]; then | |
jamfpro_url=$(defaults read $HOME/Library/Preferences/com.github.jamfpro-info jamfpro_url) | |
fi | |
if [[ -z "$jamfpro_user" ]]; then | |
jamfpro_user=$(defaults read $HOME/Library/Preferences/com.github.jamfpro-info jamfpro_user) | |
fi | |
if [[ -z "$jamfpro_password" ]]; then | |
jamfpro_password=$(defaults read $HOME/Library/Preferences/com.github.jamfpro-info jamfpro_password) | |
fi | |
fi | |
# If the Jamf Pro URL, the account username or the account password aren't available | |
# otherwise, you will be prompted to enter the requested URL or account credentials. | |
if [[ -z "$jamfpro_url" ]]; then | |
read -p "Please enter your Jamf Pro server URL : " jamfpro_url | |
fi | |
if [[ -z "$jamfpro_user" ]]; then | |
read -p "Please enter your Jamf Pro user account : " jamfpro_user | |
fi | |
if [[ -z "$jamfpro_password" ]]; then | |
read -p "Please enter the password for the $jamfpro_user account: " -s jamfpro_password | |
fi | |
echo | |
# Remove the trailing slash from the Jamf Pro URL if needed. | |
jamfpro_url=${jamfpro_url%%/} | |
GetJamfProAPIToken() { | |
# This function uses Basic Authentication to get a new bearer token for API authentication. | |
# Create base64-encoded credentials from user account's username and password. | |
encodedCredentials=$(printf "${jamfpro_user}:${jamfpro_password}" | /usr/bin/iconv -t ISO-8859-1 | /usr/bin/base64 -i –) | |
# Use the encoded credentials with Basic Authorization to request a bearer token | |
authToken=$(/usr/bin/curl "$jamfpro_url/api/v1/auth/token" –silent –request POST –header "Authorization: Basic ${encodedCredentials}") | |
# Parse the returned output for the bearer token and store the bearer token as a variable. | |
if [[ $(/usr/bin/sw_vers -productVersion | awk -F . '{print $1}') -lt 12 ]]; then | |
api_token=$(/usr/bin/awk -F \" 'NR==2{print $4}' <<< "$authToken" | /usr/bin/xargs) | |
else | |
api_token=$(/usr/bin/plutil -extract token raw -o – – <<< "$authToken") | |
fi | |
} | |
APITokenValidCheck() { | |
# Verify that API authentication is using a valid token by running an API command | |
# which displays the authorization details associated with the current API user. | |
# The API call will only return the HTTP status code. | |
api_authentication_check=$(/usr/bin/curl –write-out %{http_code} –silent –output /dev/null "$jamfpro_url/api/v1/auth" –request GET –header "Authorization: Bearer ${api_token}") | |
} | |
CheckAndRenewAPIToken() { | |
# Verify that API authentication is using a valid token by running an API command | |
# which displays the authorization details associated with the current API user. | |
# The API call will only return the HTTP status code. | |
APITokenValidCheck | |
# If the api_authentication_check has a value of 200, that means that the current | |
# bearer token is valid and can be used to authenticate an API call. | |
if [[ ${api_authentication_check} == 200 ]]; then | |
# If the current bearer token is valid, it is used to connect to the keep-alive endpoint. This will | |
# trigger the issuing of a new bearer token and the invalidation of the previous one. | |
# | |
# The output is parsed for the bearer token and the bearer token is stored as a variable. | |
authToken=$(/usr/bin/curl "$jamfpro_url/api/v1/auth/keep-alive" –silent –request POST –header "Authorization: Bearer ${api_token}") | |
if [[ $(/usr/bin/sw_vers -productVersion | awk -F . '{print $1}') -lt 12 ]]; then | |
api_token=$(/usr/bin/awk -F \" 'NR==2{print $4}' <<< "$authToken" | /usr/bin/xargs) | |
else | |
api_token=$(/usr/bin/plutil -extract token raw -o – – <<< "$authToken") | |
fi | |
else | |
# If the current bearer token is not valid, this will trigger the issuing of a new bearer token | |
# using Basic Authentication. | |
GetJamfProAPIToken | |
fi | |
} | |
InvalidateToken() { | |
# Verify that API authentication is using a valid token by running an API command | |
# which displays the authorization details associated with the current API user. | |
# The API call will only return the HTTP status code. | |
APITokenValidCheck | |
# If the api_authentication_check has a value of 200, that means that the current | |
# bearer token is valid and can be used to authenticate an API call. | |
if [[ ${api_authentication_check} == 200 ]]; then | |
# If the current bearer token is valid, an API call is sent to invalidate the token. | |
authToken=$(/usr/bin/curl "$jamfpro_url/api/v1/auth/invalidate-token" –silent –header "Authorization: Bearer ${api_token}" -X POST) | |
# Explicitly set value for the api_token variable to null. | |
api_token="" | |
fi | |
} | |
GetJamfProAPIToken | |
APITokenValidCheck | |
echo "$api_authentication_check" | |
echo "$api_token" | |
CheckAndRenewAPIToken | |
APITokenValidCheck | |
echo "$api_authentication_check" | |
echo "$api_token" | |
InvalidateToken | |
APITokenValidCheck | |
echo "$api_authentication_check" | |
echo "$api_token" |