Skip to content

Commit

Permalink
Refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
mono0926 committed Jul 30, 2017
1 parent 23374bd commit e8f0c17
Showing 1 changed file with 37 additions and 30 deletions.
67 changes: 37 additions & 30 deletions Sources/FirebaseVerifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,23 +37,19 @@ public struct FirebaseVerifier {
}
func verify(token: String, allowExpired: Bool = false) throws -> VerifiedResult {
let jwt = try JWT(token: token)
print("jwt: \(jwt)")

guard let kid = jwt.keyIdentifier else {
throw VerificationError(type: .notFound(key: "kid"), message: "Firebase ID token has no 'kid' claim.")
}
guard jwt.algorithmName == alghorithm else {
let message = "Firebase ID token has incorrect algorithm. Expected '\(alghorithm)' but got '\(String(describing: jwt.algorithmName))'. \(verifyIdTokenDocsMessage)"
throw VerificationError(type: .incorrectAlgorithm, message: message)
}
guard jwt.audience == projectId else {
let message = "Firebase ID token has incorrect 'aud' (audience) claim. Expected '\(projectId)' but got '\(String(describing: jwt.audience))'. \(projectIdMatchMessage) \(verifyIdTokenDocsMessage)"
throw VerificationError(type: .incorrectAudience, message: message)
assert(jwt.subject == jwt.userId)
if !allowExpired {
try jwt.verifyExpirationTime()
}
guard jwt.issuer == "https://securetoken.google.com/\(projectId)" else {
let message = "Firebase ID token has incorrect 'iss' (issuer) claim. Expected https://securetoken.google.com/\(projectId) but got '\(String(describing: jwt.issuer))'. \(projectIdMatchMessage) + \(verifyIdTokenDocsMessage)"
throw VerificationError(type: .incorrectIssuer, message: message)
try jwt.verifyAlgorithm()
try jwt.verifyAudience(with: projectId)
try jwt.verifyIssuer(with: projectId)

guard let keyIdentifier = jwt.keyIdentifier else {
throw VerificationError(type: .notFound(key: "kid"), message: "Firebase ID token has no 'kid' claim.")
}

guard let subject = jwt.subject else {
let message = "Firebase ID token has no 'sub' (subject) claim. \(verifyIdTokenDocsMessage)"
throw VerificationError(type: .noSub, message: message)
Expand All @@ -62,32 +58,28 @@ public struct FirebaseVerifier {
let message = "Firebase ID token has 'sub' (subject) claim longer than 128 characters. \(verifyIdTokenDocsMessage)"
throw VerificationError(type: .noSub, message: message)
}
guard let publicKey = try fetchPublicKeys()?[kid] as? String else {
let message = "Firebase ID token has 'kid' claim which does not correspond to a known public key. Most likely the ID token is expired, so get a fresh token from your client app and try again. \(verifyIdTokenDocsMessage)"
throw VerificationError(type: .notFound(key: "public key"), message: message)
}

assert(jwt.subject == jwt.userId)

let publicKeyLines = publicKey.split(separator: "\n")
let cert = String(publicKeyLines.prefix(through: publicKeyLines.count - 2).suffix(from: 1).joined())
.makeBytes()
.base64URLDecoded
let cert = try fetchPublicCertificate(with: keyIdentifier)
let signer = try RS256(x509Cert: cert)
try jwt.verifySignature(using: signer)

if !allowExpired {
try jwt.verifyExpirationTime()
}

guard let authTime = jwt.expirationTime else { throw VerificationError(type: .notFound(key: "auth_time"), message: nil) }
return VerifiedResult(userId: subject, authTime: authTime)
}

private func fetchPublicKeys() throws -> NSDictionary? {
private func fetchPublicCertificate(with keyIdentifier: String) throws -> Bytes {
// TODO: Cache-Control
let response = try String(contentsOf: URL(string: "https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com")!)
return response.toJSON() as? NSDictionary

guard let keys = response.toJSON() as? NSDictionary, let publicKey = keys[keyIdentifier] as? String else {
let message = "Firebase ID token has 'kid' claim which does not correspond to a known public key. Most likely the ID token is expired, so get a fresh token from your client app and try again. \(verifyIdTokenDocsMessage)"
throw VerificationError(type: .notFound(key: "public key"), message: message)
}

let publicKeyLines = publicKey.split(separator: "\n")
return String(publicKeyLines.prefix(through: publicKeyLines.count - 2).suffix(from: 1).joined())
.makeBytes()
.base64URLDecoded
}
}

Expand All @@ -107,6 +99,21 @@ extension JWT {
var subject: String? { return payloadStringValue(with: "sub") }
var userId: String? { return payloadStringValue(with: "user_id") }

func verifyAlgorithm() throws {
if algorithmName == alghorithm { return }
let message = "Firebase ID token has incorrect algorithm. Expected '\(alghorithm)' but got '\(String(describing: algorithmName))'. \(verifyIdTokenDocsMessage)"
throw VerificationError(type: .incorrectAlgorithm, message: message)
}
func verifyAudience(with projectId: String) throws {
if audience == projectId { return }
let message = "Firebase ID token has incorrect 'aud' (audience) claim. Expected '\(projectId)' but got '\(String(describing: audience))'. \(projectIdMatchMessage) \(verifyIdTokenDocsMessage)"
throw VerificationError(type: .incorrectAudience, message: message)
}
func verifyIssuer(with projectId: String) throws {
if issuer == "https://securetoken.google.com/\(projectId)" { return }
let message = "Firebase ID token has incorrect 'iss' (issuer) claim. Expected https://securetoken.google.com/\(projectId) but got '\(String(describing: issuer))'. \(projectIdMatchMessage) + \(verifyIdTokenDocsMessage)"
throw VerificationError(type: .incorrectIssuer, message: message)
}
func verifyExpirationTime() throws {
guard let issuedAtTime = issuedAtTime else { throw VerificationError(type: .notFound(key: "iat"), message: nil) }
guard let expirationTime = expirationTime else { throw VerificationError(type: .notFound(key: "exp"), message: nil) }
Expand Down

0 comments on commit e8f0c17

Please sign in to comment.