JamfPro API のユーザ認証の変更に備えよう
2022年の8月~12月に API のユーザ認証で Basic 認証が廃止に
JamfPro ギークの管理者にとっての1つの腕の見せ所でもありますが、JamfPro API を使って今日もゴリゴリとスクリプト書いたりされていることと思います。
さて、Jamf Developer サイトによれば今年 2022年の8月~12月のどっかで、Basic 認証が廃止され、OAuth2.0 へ移行されるという記載があることはご存知の通りと思います。
https://developer.jamf.com/jamf-pro/docs/classic-api-authentication-changes
今日は、その認証方式の変更について具体的に書いていこうと思います。
JamfPro API のユーザ認証の変更とは?
一言で言えば Basic 認証から、Bearer 認証への変更となります。
今までの Basic 認証はユーザIDとパスワードが常に全てのAPIエンドポイントつきまとうものであったのに対し、Bearer 認証は OAuth2.0 Access Token として定義された規格であり、最初の認証によって発行されたアクセストークンを、HTTPヘッダーのスキームの Authentication Bearer として指定することで、一定期間の認証と認可をトークンにまとめることができるようになります。少々大がかりなWebシステムであれば、セッション管理の中でアクセストークンを管理すれば良いので、認証・認可について思い悩む必要はなくなるメリットがある反面、JamfPro でよく使うような単機能スクリプトでは、結局ユーザID,パスワードは使う上に、Bearer アクセストークンも使わないといけないので、少々スクリプトが冗長になる可能性があります。いや、絶対なります。
Bearer アクセストークンは発行後、Basic 認証ユーザのアクセス権を持ったアクセスチケットとして json 形式(下記)で戻されます。発行されたタイイングで有効期限(30分)も設定されますが、使い終わったら必ず無効化をしなければならないことに注意してください。ロジック次第ですが、アクセストークンは何らかのタイミングで無効化+再発行しても良いですし、使い方は作り手次第ってところもあります。
{
"token": "eyJhbGciOiJIUzUxMiJ9...",
"expires": "2022-01-24T21:35:20.373Z"
}
これから記載するサンプルコードの動作は確認してありますが、動作保証はしませんので自己責任でご利用ください。また、スペースの関係上エラー処理はしておりませんので、戻り値については下記リンクを参照の上、エラー処理を組み込んでいただければと思います。
https://developer.jamf.com/jamf-pro/docs/jamf-pro-api-overview
/api/v1/auth/token
|
POST
|
Basic認証を用いた Bearer アクセストークンの取得
|
/api/v1/auth
|
GET
|
Bearer アクセストークンの有効性の確認
|
/api/v1/auth/keep-alive
|
POST
|
Bearer アクセストークンの更新(無効+再発行)
|
/api/v1/auth/invalidate-token
|
POST
|
Bearer アクセストークンの無効化
|
ケース1: bash スクリプトからの JamfPro API アクセス
#!/bin/bash
udid=$( ioreg -rd1 -c IOPlatformExpertDevice | awk '/IOPlatformUUID/{print $3}' | tr -d '"' )
jssurl='/usr/bin/defaults read /Library/Preferences/com.jamfsoftware.jamf.plist jss_url'
apiuser=""
apipass=""
// Basic認証で MDM Capable User を取得します
user=$( curl -k -H "Accept: application/xml" -s -u ${apiuser}:${apipass} ${jssurl}JSSResource/computers/udid/${udid} | xmllint --xpath '/computer/general/mdm_capable_users/mdm_capable_user/text()' -)
if [ "$user" = "" ];
then
echo "<result>Missing</result>"
else
echo "<result>$user</result>"
fi
exit 0
結局、スクリプトの中にユーザID、パスワード、Bearer トークンが勢揃いするので何かモヤモヤしますが・・・戻り値は json 形式ですが、パースのために Python モジュールを使っています。
#!/bin/bash
udid=$( ioreg -rd1 -c IOPlatformExpertDevice | awk '/IOPlatformUUID/{print $3}' | tr -d '"' )
jssurl='/usr/bin/defaults read /Library/Preferences/com.jamfsoftware.jamf.plist jss_url'
apiuser=""
apipass=""
# Bearer トークンを取得します
jssapitoken=$( curl -X POST -k -u ${apiuser}:${apipass} -s ${jssurl}api/v1/auth/token -H "Accept: application/json" -H "Accept-Language:ja,en-us;q=0.7,en;q=0.3" | python3 -c "import sys, json; print(json.load(sys.stdin)['token'])" )
// Bearer トークンで MDM Capable User を取得します
user=$( curl -k -H "Accept: application/xml" -H "Authorization: Bearer ${jssapitoken}" -s ${jssurl}JSSResource/computers/udid/${udid} | xmllint --xpath '/computer/general/mdm_capable_users/mdm_capable_user/text()' -)
// Bearer トークンを無効に
res=$( curl -X POST -k -H "Accept: application/json" -H "Authorization: Bearer ${jssapitoken}" -s ${jssurl}api/v1/auth/invalidate-token )
if [ "$user" = "" ];
then
echo "<result>Missing</result>"
else
echo "<result>$user</result>"
fi
exit 0
# Bearer トークンを取得します
jssapitoken=$( curl -X POST -k -u ${apiuser}:${apipass} -s ${jssurl}api/v1/auth/token -H "Accept: application/json" -H "Accept-Language:ja,en-us;q=0.7,en;q=0.3" | jq -r '.token' )
ケース2: PHP からの JamfPro API アクセス
<?php
// $bPost -- TRUE:POST FALSE:GET
function curlsession($url,$headers,$bPost,&$res)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_POST, $bPost);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$res = curl_exec($ch);
var_dump($res);
curl_close($ch);
}
// Barer トークンを取得(POST)
$jssurl="https://COPORATE.jamfcloud.com/";
$apiuserpass="apiuser:apipass";
$encodedCredentials = base64_encode($apiuserpass);
$headers = array(
"Accept: application/json",
"Accept-Language:ja,en-us;q=0.7,en;q=0.3",
"Authorization: Basic ${encodedCredentials}"
);
$url = "${jssurl}api/v1/auth/token";
curlsession($url,$headers,TRUE,$rres);
unset($headers);
// json を連想配列にセット
$tokenarray = json_decode($rres,true);
$jamftoken= $tokenarray['token'];
// モバイルデバイスの一覧を取得(GET)
$headers = array(
"Accept: application/json",
"Authorization: Bearer ${jamftoken}"
);
$url = "${jssurl}JSSResource/mobiledevices";
curlsession($url,$headers,FALSE,$rres);
unset($headers);
// Bearer トークンを無効に(POST)
$headers = array(
"Accept: application/json",
"Accept-Language:ja,en-us;q=0.7,en;q=0.3",
"Authorization: Bearer ${jamftoken}"
);
$url = "${jssurl}api/v1/auth/invalidate-token";
curlsession($url,$headers,TRUE,$rres);
unset($headers);
?>
まとめ
なんとなくですが、今後の単機能スクリプトはスクリプト単位に認証するのではなく、認証するためのユーザID,パスワードは base64 エンコードしたものを Mac のどっかの plist にでも入れておいて、それを定期的に別スクリプトでメンテナンスしつつ、その plist から Basic 認証情報をとって Bearer アクセストークンを取得するみたいな流れになるのが綺麗な気がするなぁ。
ま、そんなわけで、皆さんも来る Basic認証廃止の日まで、新しい認証方式へ対応いただけますと幸いです。
c u !!