JWT(JSON Web Token) ์ ๋ณด
์ฑ์ผ๋ก ์ธ์ฆํ๊ฑฐ๋ ์ค์น ์ก์ธ์ค ํ ํฐ์ ์์ฑํ๋ ค๋ฉด JWT(JSON Web Token)๋ฅผ ์์ฑํด์ผ ํฉ๋๋ค. REST API ์๋ํฌ์ธํธ๊ฐ JWT๋ฅผ ํ์ํ ๊ฒฝ์ฐ ํด๋น ์๋ํฌ์ธํธ์ ๋ํ ์ค๋ช ์์๋ JWT๋ฅผ ์ฌ์ฉํ์ฌ ์๋ํฌ์ธํธ์ ์ก์ธ์คํด์ผ ํจ์ ๋ํ๋ ๋๋ค.
JWT๋ RS256
์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํ์ฌ ์๋ช
๋์ด์ผ ํ๋ฉฐ ๋ค์ ํด๋ ์์ ํฌํจํด์ผ ํฉ๋๋ค.
ํด๋ ์ | ์๋ฏธ | ์ธ๋ถ ์ ๋ณด |
---|---|---|
iat | ๋ฐ๊ธ ์๊ฐ | JWT๊ฐ ๋ง๋ค์ด์ง ์๊ฐ์ ๋๋ค. ํด๋ก ๋๋ฆฌํํธ๋ฅผ ๋ฐฉ์งํ๋ ค๋ฉด ์ด๋ฅผ ๊ณผ๊ฑฐ 60์ด๋ก ์ค์ ํ๊ณ ์๋ฒ์ ๋ ์ง์ ์๊ฐ์ ์ ํํ๊ฒ ์ค์ ํ๋ ๊ฒ์ด ์ข์ต๋๋ค(์: ๋คํธ์ํฌ ํ์ ํ๋กํ ์ฝ ์ฌ์ฉ). |
exp | ๋ง๋ฃ ์๊ฐ | JWT์ ๋ง๋ฃ ์๊ฐ์ด๋ฉฐ, ๊ทธ ํ์๋ ์ค์น ํ ํฐ์ ์์ฒญํ๋ ๋ฐ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์๊ฐ์ ์์ผ๋ก 10๋ถ ์ด๋ด์ฌ์ผ ํฉ๋๋ค. |
iss | ๋ฐ๊ธ์ | GitHub App์ ํด๋ผ์ด์ธํธ ID ๋๋ ์ ํ๋ฆฌ์ผ์ด์ ID์ ๋๋ค. ์ด ๊ฐ์ JWT์ ์๋ช ์ ํ์ธํ๋ ๋ฐ ์ ํฉํ ๊ณต๊ฐ ํค๋ฅผ ์ฐพ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค. GitHub App์ ์ค์ ํ์ด์ง์์ ์ฑ์ ID๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค. ํด๋ผ์ด์ธํธ ID๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค. GitHub App์ ์ค์ ํ์ด์ง๋ก ์ด๋ํ๋ ๋ฐฉ๋ฒ์ ๋ํ ์์ธํ ๋ด์ฉ์ GitHub ์ฑ ๋ฑ๋ก ์์ ์(๋ฅผ) ์ฐธ์กฐํ์ธ์. |
alg | ๋ฉ์์ง ์ธ์ฆ ์ฝ๋ ์๊ณ ๋ฆฌ์ฆ | ์ด๊ฒ์ RS256 ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํ์ฌ JWT๊ฐ ์๋ช
๋์ด์ผ ํ๊ธฐ ๋๋ฌธ์ RS256 ์ฌ์ผ ํฉ๋๋ค. |
JWT๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด API ์์ฒญ์ Authorization
ํค๋์ ์ ๋ฌํฉ๋๋ค. ์์:
curl --request GET \
--url "https://api.github.com/app" \
--header "Accept: application/vnd.github+json" \
--header "Authorization: Bearer YOUR_JWT" \
--header "X-GitHub-Api-Version: 2022-11-28"
๋๋ถ๋ถ์ ๊ฒฝ์ฐ Authorization: Bearer
๋๋ Authorization: token
์ ์ฌ์ฉํ์ฌ ์ ๋ฌํ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ JWT(JSON ์น ํ ํฐ)๋ฅผ ์ ๋ฌํ๋ ๊ฒฝ์ฐ Authorization: Bearer
๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
JWT(JSON Web Token) ์์ฑ
๋๋ถ๋ถ์ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์๋ JWT๋ฅผ ์์ฑํ ์ ์๋ ํจํค์ง๊ฐ ์์ต๋๋ค. ๋ชจ๋ ๊ฒฝ์ฐ์ GitHub App์ ํ๋ผ์ด๋น ํค์ ID๊ฐ ์์ด์ผ ํฉ๋๋ค. ํ๋ผ์ด๋น ํค ์์ฑ์ ๊ดํ ์์ธํ ๋ด์ฉ์ GitHub ์ฑ์ ๋ํ ํ๋ผ์ด๋น ํค ๊ด๋ฆฌ์(๋ฅผ) ์ฐธ์กฐํ์ธ์. GET /app
REST API ์๋ํฌ์ธํธ๋ฅผ ์ฌ์ฉํ์ฌ ์ฑ์ ID๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค. ์์ธํ ๋ด์ฉ์ REST API ์ค๋ช
์์ ์๋ ์ฑ์ ์ฐธ์กฐํ์ธ์.
์ฐธ๊ณ ํญ๋ชฉ
JWT๋ฅผ ๋ง๋๋ ๋์ GitHub์ Oktokit SDK๋ฅผ ์ฌ์ฉํ์ฌ ์ฑ์ผ๋ก ์ธ์ฆํ ์ ์์ต๋๋ค. SDK๋ JWT ์์ฑ์ ์ฒ๋ฆฌํ๊ณ ํ ํฐ์ด ๋ง๋ฃ๋๋ฉด JWT๋ฅผ ๋ค์ ์์ฑํฉ๋๋ค. ์์ธํ ๋ด์ฉ์ REST API ๋ฐ JavaScript๋ฅผ ์ฌ์ฉํ์ฌ ์คํฌ๋ฆฝํ ์ ์ฐธ์กฐํ์ธ์.
์: ๋ฃจ๋น๋ฅผ ์ฌ์ฉํ์ฌ JWT ์์ฑ
์ฐธ๊ณ ํญ๋ชฉ
์ด ์คํฌ๋ฆฝํธ๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด jwt
ํจํค์ง๋ฅผ ์ค์นํ๊ธฐ ์ํด gem install jwt
๋ฅผ ์คํํด์ผ ํฉ๋๋ค.
๋ค์ ์์ ์์๋ YOUR_PATH_TO_PEM
์(๋ฅผ) ํ๋ผ์ด๋น ํค๊ฐ ์ ์ฅ๋ ํ์ผ ๊ฒฝ๋ก๋ก ๋ฐ๊ฟ๋๋ค. YOUR_CLIENT_ID
์(๋ฅผ) ์ฑ์ ID์ผ๋ก ๋ฐ๊ฟ๋๋ค. YOUR_PATH_TO_PEM
๋ฐ YOUR_CLIENT_ID
๊ฐ์ ํฐ๋ฐ์ดํ๋ก ๋ฌถ์ด์ผ ํฉ๋๋ค.
require 'openssl'
require 'jwt' # https://rubygems.org/gems/jwt
# Private key contents
private_pem = File.read("YOUR_PATH_TO_PEM")
private_key = OpenSSL::PKey::RSA.new(private_pem)
# Generate the JWT
payload = {
# issued at time, 60 seconds in the past to allow for clock drift
iat: Time.now.to_i - 60,
# JWT expiration time (10 minute maximum)
exp: Time.now.to_i + (10 * 60),
# GitHub App's client ID
iss: "YOUR_CLIENT_ID"
}
jwt = JWT.encode(payload, private_key, "RS256")
puts jwt
์: ํ์ด์ฌ์ ์ฌ์ฉํ์ฌ JWT ์์ฑ
์ฐธ๊ณ ํญ๋ชฉ
์ด ์คํฌ๋ฆฝํธ๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด PyJWT
๋ฐ cryptography
ํจํค์ง๋ฅผ ์ค์นํ๊ธฐ ์ํด pip install PyJWT cryptography
๋ฅผ ์คํํด์ผ ํฉ๋๋ค.
#!/usr/bin/env python3 import sys import time import jwt # Get PEM file path if len(sys.argv) > 1: pem = sys.argv[1] else: pem = input("Enter path of private PEM file: ") # Get the Client ID if len(sys.argv) > 2: client_id = sys.argv[2] else: client_id = input("Enter your Client ID: ") # Open PEM with open(pem, 'rb') as pem_file: signing_key = pem_file.read() payload = { # Issued at time 'iat': int(time.time()), # JWT expiration time (10 minutes maximum) 'exp': int(time.time()) + 600, # GitHub App's client ID 'iss': client_id } # Create JWT encoded_jwt = jwt.encode(payload, signing_key, algorithm='RS256') print(f"JWT: {encoded_jwt}")
#!/usr/bin/env python3
import sys
import time
import jwt
# Get PEM file path
if len(sys.argv) > 1:
pem = sys.argv[1]
else:
pem = input("Enter path of private PEM file: ")
# Get the Client ID
if len(sys.argv) > 2:
client_id = sys.argv[2]
else:
client_id = input("Enter your Client ID: ")
# Open PEM
with open(pem, 'rb') as pem_file:
signing_key = pem_file.read()
payload = {
# Issued at time
'iat': int(time.time()),
# JWT expiration time (10 minutes maximum)
'exp': int(time.time()) + 600,
# GitHub App's client ID
'iss': client_id
}
# Create JWT
encoded_jwt = jwt.encode(payload, signing_key, algorithm='RS256')
print(f"JWT: {encoded_jwt}")
์ด ์คํฌ๋ฆฝํธ๋ ํ๋ผ์ด๋น ํค๊ฐ ์ ์ฅ๋ ํ์ผ ๊ฒฝ๋ก์ ์ฑ์ ID๋ฅผ ํ์ํฉ๋๋ค. ๋๋ ์คํฌ๋ฆฝํธ๋ฅผ ์คํํ ๋ ์ด๋ฌํ ๊ฐ์ ์ธ๋ผ์ธ ์ธ์๋ก ์ ๋ฌํ ์ ์์ต๋๋ค.
์์ : Bash๋ฅผ ์ฌ์ฉํ์ฌ JWT ์์ฑ
์ฐธ๊ณ ํญ๋ชฉ
์ด ์คํฌ๋ฆฝํธ๋ฅผ ์คํํ ๋ ํด๋ผ์ด์ธํธ ID์ ํ๋ผ์ด๋น ํค๊ฐ ์ ์ฅ๋๋ ํ์ผ ๊ฒฝ๋ก๋ฅผ ์ธ์๋ก ์ ๋ฌํด์ผ ํฉ๋๋ค.
#!/usr/bin/env bash set -o pipefail client_id=$1 # Client ID as first argument pem=$( cat $2 ) # file path of the private key as second argument now=$(date +%s) iat=$((${now} - 60)) # Issues 60 seconds in the past exp=$((${now} + 600)) # Expires 10 minutes in the future b64enc() { openssl base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n'; } header_json='{ "typ":"JWT", "alg":"RS256" }' # Header encode header=$( echo -n "${header_json}" | b64enc ) payload_json="{ \"iat\":${iat}, \"exp\":${exp}, \"iss\":\"${client_id}\" }" # Payload encode payload=$( echo -n "${payload_json}" | b64enc ) # Signature header_payload="${header}"."${payload}" signature=$( openssl dgst -sha256 -sign <(echo -n "${pem}") \ <(echo -n "${header_payload}") | b64enc ) # Create JWT JWT="${header_payload}"."${signature}" printf '%s\n' "JWT: $JWT"
#!/usr/bin/env bash
set -o pipefail
client_id=$1 # Client ID as first argument
pem=$( cat $2 ) # file path of the private key as second argument
now=$(date +%s)
iat=$((${now} - 60)) # Issues 60 seconds in the past
exp=$((${now} + 600)) # Expires 10 minutes in the future
b64enc() { openssl base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n'; }
header_json='{
"typ":"JWT",
"alg":"RS256"
}'
# Header encode
header=$( echo -n "${header_json}" | b64enc )
payload_json="{
\"iat\":${iat},
\"exp\":${exp},
\"iss\":\"${client_id}\"
}"
# Payload encode
payload=$( echo -n "${payload_json}" | b64enc )
# Signature
header_payload="${header}"."${payload}"
signature=$(
openssl dgst -sha256 -sign <(echo -n "${pem}") \
<(echo -n "${header_payload}") | b64enc
)
# Create JWT
JWT="${header_payload}"."${signature}"
printf '%s\n' "JWT: $JWT"
์์ : PowerShell์ ์ฌ์ฉํ์ฌ JWT ์์ฑ
๋ค์ ์์ ์์๋ YOUR_PATH_TO_PEM
์(๋ฅผ) ํ๋ผ์ด๋น ํค๊ฐ ์ ์ฅ๋ ํ์ผ ๊ฒฝ๋ก๋ก ๋ฐ๊ฟ๋๋ค. YOUR_CLIENT_ID
์(๋ฅผ) ์ฑ์ ID์ผ๋ก ๋ฐ๊ฟ๋๋ค. YOUR_PATH_TO_PEM
๊ฐ์ ํฐ๋ฐ์ดํ๋ก ๋ฌถ์ด์ผ ํฉ๋๋ค.
#!/usr/bin/env pwsh $client_id = YOUR_CLIENT_ID $private_key_path = "YOUR_PATH_TO_PEM" $header = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json -InputObject @{ alg = "RS256" typ = "JWT" }))).TrimEnd('=').Replace('+', '-').Replace('/', '_'); $payload = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json -InputObject @{ iat = [System.DateTimeOffset]::UtcNow.AddSeconds(-10).ToUnixTimeSeconds() exp = [System.DateTimeOffset]::UtcNow.AddMinutes(10).ToUnixTimeSeconds() iss = $client_id }))).TrimEnd('=').Replace('+', '-').Replace('/', '_'); $rsa = [System.Security.Cryptography.RSA]::Create() $rsa.ImportFromPem((Get-Content $private_key_path -Raw)) $signature = [Convert]::ToBase64String($rsa.SignData([System.Text.Encoding]::UTF8.GetBytes("$header.$payload"), [System.Security.Cryptography.HashAlgorithmName]::SHA256, [System.Security.Cryptography.RSASignaturePadding]::Pkcs1)).TrimEnd('=').Replace('+', '-').Replace('/', '_') $jwt = "$header.$payload.$signature" Write-Host $jwt
#!/usr/bin/env pwsh
$client_id = YOUR_CLIENT_ID
$private_key_path = "YOUR_PATH_TO_PEM"
$header = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json -InputObject @{
alg = "RS256"
typ = "JWT"
}))).TrimEnd('=').Replace('+', '-').Replace('/', '_');
$payload = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes((ConvertTo-Json -InputObject @{
iat = [System.DateTimeOffset]::UtcNow.AddSeconds(-10).ToUnixTimeSeconds()
exp = [System.DateTimeOffset]::UtcNow.AddMinutes(10).ToUnixTimeSeconds()
iss = $client_id
}))).TrimEnd('=').Replace('+', '-').Replace('/', '_');
$rsa = [System.Security.Cryptography.RSA]::Create()
$rsa.ImportFromPem((Get-Content $private_key_path -Raw))
$signature = [Convert]::ToBase64String($rsa.SignData([System.Text.Encoding]::UTF8.GetBytes("$header.$payload"), [System.Security.Cryptography.HashAlgorithmName]::SHA256, [System.Security.Cryptography.RSASignaturePadding]::Pkcs1)).TrimEnd('=').Replace('+', '-').Replace('/', '_')
$jwt = "$header.$payload.$signature"
Write-Host $jwt