此文分為幾個部分:
[PHP] OPENID CONNECT #0 簡介及取得URL
[PHP] OPENID CONNECT #1 取得ACCESS TOKEN
[PHP] OPENID CONNECT #2 取得USERINFO及PROFILE
[PHP] OPENID CONNECT #3 驗證id_token
[PHP] OPENID CONNECT #4 程式下載及安裝
在#1 取得ACCESS TOKEN #2 取得USERINFO及PROFILE兩篇文件中ACCESS TOKEN取回時也有得到 id_token的欄位,id_token的欄位是JSON Web Token(JWT)的格式
id_token介紹
1. BASE64 編碼
2. 分成三個部分,中間用 '.'作區分
3. 一個JWT範例。其中第1部分(青色)寫的是簽章方式,第2部分(綠色)是OP送出來部分的欄位CLAIMS,第3部分(黃色)是由第1、第2兩部分簽章後的結果,2進位內容
經由BASE64 解碼後內容範例為:
4. 欄位說明
alg 簽章方式,採用 RS256方式簽章,對稱金鑰
sub subject 帳號唯一識別碼
aud audience 聽眾就是你的client_id
iss issuer id_token發行者
exp 有效時間 timestamp
iat issue at time 簽發時間 timestamp iat+3600=exp
產生公鑰
和SSL有關的,可用來驗證ID TOKEN的資料在OPENID CONNECT #0 簡介及取得URL此文中有:
"jwks_uri" : "https://oidc.tanet.edu.tw/oidc/v1/jwksets"
其內容為
取其中的 use:"sig"的key值module(n)和exponents(e)來演算公錀(public key)
PHP
使用modulus(n)和exponent(e)來產生金鑰, 需要安裝套件phpseclib,程式碼
<?php use phpseclib\Crypt\RSA; use phpseclib\Math\BigInteger; //取回access token include "config.php"; include "library.class.php"; include('./vendor/autoload.php'); //驗證 id_token 所需套件 $obj= new openid(); $jwks_uri=JWKS_URI; if(DYNAMICAL_ENDPOINT){ $jwks_uri=$ep->getEndPoint()->jwks_uri; } $jd = $obj->getModnExp($jwks_uri); if( !$jd) { die ("無法取得 USER INFO"); } $e= $jd['keys'][0]['e']; $n= $jd['keys'][0]['n']; // 取得public key,需安裝套件 // https://github.com/phpseclib/phpseclib $rsa = new RSA(); $nx = new BigInteger($obj->urlsafeB64Decode($n), 256); $ex = new BigInteger($obj->urlsafeB64Decode($e), 256); $rsa->loadKey( array( 'e' => $ex, 'n' => $nx ) ); $pubkey= $rsa->getPublicKey();
其中 $pubkey輸出結果
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmzzA8UMrmiFxqA4fCQWx
/BGwxkpZ5QrTBkQkHBpQ0piISte7VS+xbvcgFdrT6Gzsq+f36HgSO8pwHPkioJak
lo/4ro/VgIWStBd2Jjd6kDEa5Kzbyc/9/FRDtjXA/giiMwTIqHQwYsAWgOrNw7a/
J9DGOPhtwfnHNRu30iAtWMga3iGh1vcWOVSZyl/xzFBuLb0hYdaKG8beJ+qp+hiS
QqxCAcEapeXoMwAVomlpaQDsHwyNigW+CxTVSNfbZxLCmMabEPqBJSnAkiHaSrv8
/ATBHi+LAF3JIIzD5qGUgp1vVDVKEm9HrlIvOO7spyyCw/I4CdSZXS2vERyMSTit
vQIDAQAB
-----END PUBLIC KEY-----
接下來要由public key來驗證 id_token
驗證id_token
取得 public key後就可驗證 id_token, 需要安裝套件namshi/jose,這個套件對於PHP>=7.0有支持上的問題,會出現錯誤:
Fatal error: Uncaught InvalidArgumentException: phpseclib 1.0.0(LTS), even the latest 2.0.0, doesn't support PHP7 yet in /.../vendor/namshi/jose/src/Namshi/JOSE/JWS.php:xxx
需要修改原始碼 JWS.php把限制改掉
修改 ./vendor/namshi/jose/src/Namshi/JOSE/JWS.php 把第41-43行註解
PHP
驗證 id_token
// 驗證id token,需安裝套件 // composer require namshi/jose $id_token = $_SESSION['id_token']; $JWS= new JWS(array(),'SecLib'); $jws = $JWS->load($id_token); $p=$jws->getPayload();; //取得payload if ($jws->verify($pubkey, 'RS256')) { echo sprintf("ID_TOKEN驗證通過,USER UNIQUE ID= #%s", $p['sub']); }else{ echo "ID_TOKEN驗證失敗"; }
執行結果
參考資料
[1] 驗證JWT https://jwt.io/
[2] https://jwt.io/introduction/
[3] http://phpseclib.sourceforge.net/rsa/intro.html
[4] https://stackoverflow.com/questions/16993838/openssl-how-can-i-get-public-key-from-modulus