class MSIDP::CertificateCredential

Certificate credential for application authentication

Attributes

client_id[RW]

@return [String] client_id the assigned applicaiton (client) ID.

tenant[RW]

@return [String] tenant a directory tenant in GUID or domain-name format.

Public Class Methods

new(cert, key, tenant:, client_id:) click to toggle source

Initialize an instance

@param [OpenSSL::X509::Certificate] cert a certificate. @param [OpenSSL::PKey] cert the private key paired with the certificate. @param [String] tenant a directory tenant in GUID or domain-name format. @param [String] client_id the assigned applicaiton (client) ID.

# File lib/msidp/certificate_credential.rb, line 21
def initialize(cert, key, tenant:, client_id:)
  @cert = cert
  @key = key
  @tenant = tenant
  @client_id = client_id
end

Public Instance Methods

assertion() click to toggle source

Computes the JWT assertion.

@return [String] JWT assertion string.

# File lib/msidp/certificate_credential.rb, line 31
def assertion
  header_base64 = base64url_encode(header)
  payload_base64 = base64url_encode(payload)
  signature = @key.sign('sha256', "#{header_base64}.#{payload_base64}")
  sign_base64 = base64url_encode(signature)
  "#{header_base64}.#{payload_base64}.#{sign_base64}"
end
header() click to toggle source

JOSE header of the JWT.

@return [String] JSON string of the JOSE header.

# File lib/msidp/certificate_credential.rb, line 42
def header
  digest = OpenSSL::Digest::SHA1.digest(@cert.to_der)
  x5t = Base64.urlsafe_encode64(digest)
  header = { alg: 'RS256', typ: 'JWT', x5t: x5t.to_s }
  JSON.dump(header)
end
payload() click to toggle source

JWS payload of the JWT claim.

@return [String] JSON string of the JWS payload.

# File lib/msidp/certificate_credential.rb, line 52
def payload
  not_after = @cert.not_after.to_i
  not_before = @cert.not_before.to_i
  jti = make_jwt_id
  payload = {
    aud: "https://login.microsoftonline.com/#{tenant}/v2.0",
    exp: not_after, iss: client_id, jti: jti,
    nbf: not_after, sub: client_id, iat: not_before
  }
  JSON.dump(payload)
end

Private Instance Methods

base64url_encode(data) click to toggle source
# File lib/msidp/certificate_credential.rb, line 66
def base64url_encode(data)
  Base64.urlsafe_encode64(data, padding: false)
end
make_jwt_id() click to toggle source
# File lib/msidp/certificate_credential.rb, line 70
def make_jwt_id
  data = @cert.to_der
  data << tenant
  data << client_id
  sha1hash_uuid(OpenSSL::Digest::SHA1.digest(data))
end
sha1hash_uuid(hash) click to toggle source

rubocop:disable Style/FormatStringToken

# File lib/msidp/certificate_credential.rb, line 78
def sha1hash_uuid(hash)
  bytes = hash.bytes[0..15]
  bytes[6] = bytes[6] & 0x0F | 0x50
  bytes[8] = bytes[6] & 0x3F | 0x80
  format(
    '%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x',
    *bytes
  )
end