Skip to content

Commit

Permalink
Merge pull request #52 from 3sidedcube/fix/SP-771-request-credential
Browse files Browse the repository at this point in the history
Fix `RequestCredential` issue resulting from insecure encoding/decoding
  • Loading branch information
BenShutt authored Aug 25, 2022
2 parents d338e71 + 2fc6991 commit 8b8e9b0
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 31 deletions.
2 changes: 1 addition & 1 deletion ThunderRequest/CredentialStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ public struct CredentialStore {
return nil
}

return RequestCredential.init(keychainData: data)
return try? RequestCredential(keychainData: data)
}

/// Deletes an entry for a certain identifier from the keychain
Expand Down
72 changes: 42 additions & 30 deletions ThunderRequest/RequestCredential.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,47 +21,53 @@ public let kTSCAuthServiceName = "TSCAuthCredential"

/// A class used to store authentication information and return the `URLCredential` object when required
@objc(TSCRequestCredential)
public final class RequestCredential: NSObject, NSCoding {
public final class RequestCredential: NSObject, NSSecureCoding {

/// Returns the url credential which can be used to authenticate a request
public var credential: URLCredential?

public var credential: URLCredential? {
guard let username = username else { return nil }
guard let password = password else { return nil }
return URLCredential(user: username, password: password, persistence: .none)
}

/// The username to auth the user with
public var username: String?

/// The password to auth the user with
public var password: String?

/// The auth token to auth the user with
public var authorizationToken: String?

/// The type of the token
public var tokenType: String = Authentication.TokenType.bearer

/// The date on which the authorization token expires
public var expirationDate: Date?

/// The refresh token to be sent back to the authenticating endpoint for certain auth methods
public var refreshToken: String?

/// Init method for re-constructing from data stored in the user's keychain
///
/// - Parameter keychainData: The data which was retrieved from the keychain
init?(keychainData: Data) {
guard let credential = try? NSKeyedUnarchiver.unarchivedObject(
ofClass: RequestCredential.self,
init(keychainData: Data) throws {
// Root object RequestCredential and other encoded types
let requestCredential = try NSKeyedUnarchiver.unarchivedObject(
ofClasses: [RequestCredential.self, NSString.self, NSDate.self],
from: keychainData
) else {
return nil
)

guard let credential = requestCredential as? RequestCredential else {
throw RequestCredentialError.invalidType
}

self.authorizationToken = credential.authorizationToken
self.credential = credential.credential
self.username = credential.username
self.password = credential.password
self.tokenType = credential.tokenType
}

/// Whether the credential has expired. Where expiryDate is missing this will return as false, as it is
/// assumed the credential doesn't have an expiry date in this case
public var hasExpired: Bool {
Expand All @@ -70,27 +76,26 @@ public final class RequestCredential: NSObject, NSCoding {
}
return Date() > expiry
}

/// The data to store in the keychain
public func keychainData() throws -> Data {
return try NSKeyedArchiver.archivedData(
withRootObject: self,
requiringSecureCoding: false
)
}

/// Creates a new username/password based credential
///
/// - Parameters:
/// - username: The username of the authorization object
/// - password: The password of the authorization object
public init(username: String, password: String) {
super.init()
credential = URLCredential(user: username, password: password, persistence: .none)
self.username = username
self.password = password
}

/// Initialises a new OAuth2 credential with given parameters
///
/// - Parameters:
Expand All @@ -99,50 +104,57 @@ public final class RequestCredential: NSObject, NSCoding {
/// - expiryDate: The date upon which the credential will expire for the user.
/// - tokenType: The token type of the credential (Defaults to Bearer)
public init(authorizationToken: String, refreshToken: String?, expiryDate: Date, tokenType: String = "Bearer") {

self.refreshToken = refreshToken
self.expirationDate = expiryDate
self.authorizationToken = authorizationToken
self.tokenType = tokenType
super.init()
}

/// Creates a new auth token based credential
///
/// - Parameter authorizationToken: The authorization token to use
init(authorizationToken: String) {
super.init()
self.authorizationToken = authorizationToken
}

private enum CodingKeys: String {
case username
case password
case authToken = "authtoken"
case credential
case tokenType = "tokentype"
case expiration
case refreshToken = "refreshtoken"
}

public func encode(with aCoder: NSCoder) {
aCoder.encode(username, forKey: CodingKeys.username.rawValue)
aCoder.encode(password, forKey: CodingKeys.password.rawValue)
aCoder.encode(authorizationToken, forKey: CodingKeys.authToken.rawValue)
aCoder.encode(credential, forKey: CodingKeys.credential.rawValue)
aCoder.encode(tokenType, forKey: CodingKeys.tokenType.rawValue)
aCoder.encode(expirationDate, forKey: CodingKeys.expiration.rawValue)
aCoder.encode(refreshToken, forKey: CodingKeys.refreshToken.rawValue)
}

required public init?(coder aDecoder: NSCoder) {
super.init()
username = aDecoder.decodeObject(forKey: CodingKeys.username.rawValue) as? String
password = aDecoder.decodeObject(forKey: CodingKeys.password.rawValue) as? String
authorizationToken = aDecoder.decodeObject(forKey: CodingKeys.authToken.rawValue) as? String
credential = aDecoder.decodeObject(forKey: CodingKeys.credential.rawValue) as? URLCredential
tokenType = aDecoder.decodeObject(forKey: CodingKeys.tokenType.rawValue) as? String ?? Authentication.TokenType.bearer
refreshToken = aDecoder.decodeObject(forKey: CodingKeys.refreshToken.rawValue) as? String
expirationDate = aDecoder.decodeObject(forKey: CodingKeys.expiration.rawValue) as? Date
}

// MARK: - NSSecureCoding

public static var supportsSecureCoding: Bool {
return true
}
}

enum RequestCredentialError: Error {
case invalidType
}

0 comments on commit 8b8e9b0

Please sign in to comment.