Skip to content

Commit

Permalink
Release version 3.1.3
Browse files Browse the repository at this point in the history
  • Loading branch information
Team Mobile Schorsch committed Mar 11, 2024
1 parent 384ea40 commit 36a05b2
Show file tree
Hide file tree
Showing 7 changed files with 262 additions and 193 deletions.
4 changes: 2 additions & 2 deletions Documentation/source/Installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ Once you have your Swift package set up, adding `GiniBankAPILibrary` as a depend

```swift
dependencies: [
.package(url: "https://github.com/gini/bank-api-library-ios.git", .exact("3.1.2"))
.package(url: "https://github.com/gini/bank-api-library-ios.git", .exact("3.1.3"))
]
```

In case that you want to use the certificate pinning in the library, add `GiniBankAPILibraryPinning`:
```swift
dependencies: [
.package(url: "https://github.com/gini/bank-api-library-pinning-ios.git", .exact("3.1.2"))
.package(url: "https://github.com/gini/bank-api-library-pinning-ios.git", .exact("3.1.3"))
]
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,15 @@ extension SessionManager: SessionAuthenticationProtocol {

return User(email: email, password: password)
}

func logIn(completion: @escaping CompletionResult<Token>) {

let saveTokenAndComplete: (Result<Token, GiniError>) -> Void = { result in

switch result {
case .failure: break
case .failure:
self.removeUserAccessToken()
case .success(let token):

do {
try self.keyStore.save(item: KeychainManagerItem(key: .userAccessToken,
value: token.accessToken,
service: .auth))

} catch {
preconditionFailure("Gini couldn't safely save the user credentials in the Keychain. " +
"Enable the 'Keychain Sharing' entitlement in your app")
}
self.userAccessToken = token.accessToken
}

completion(result)
}

Expand All @@ -53,25 +42,84 @@ extension SessionManager: SessionAuthenticationProtocol {
} else {

if let user = user {

fetchUserAccessToken(for: user, completion: saveTokenAndComplete)


fetchUserAccessToken(for: user) { result in
switch result {
case .success:
saveTokenAndComplete(result)
case .failure(let error):
if case .unauthorized = error {
self.removeCurrentUserInfo()
self.createUser { result in
switch result {
case .success(let user):
self.fetchUserAccessToken(for: user, completion: saveTokenAndComplete)
case .failure(let error):
completion(.failure(error))
}
}
} else {
completion(.failure(error))
}
}
}
} else {
createUser { result in
switch result {
case .success(let user):
self.fetchUserAccessToken(for: user, completion: saveTokenAndComplete)
case .failure(let error):
completion(.failure(error))
case .success(let user):
self.fetchUserAccessToken(for: user, completion: saveTokenAndComplete)
case .failure(let error):
completion(.failure(error))
}
}
}
}
}

func logOut() {
// Remove current user info from SessionManager
userAccessToken = nil
clientAccessToken = nil

// Remove current user info from Keychain
keyStore.removeAll()
}

private func removeUserAccessToken() {
// Remove current userAccessToken from SessionManager
userAccessToken = nil

// removing `userAccessToken` from Keychain is part of the old implementation where it was saved in Keychain
do {
try KeychainStore().remove(service: .auth, key: .userAccessToken)
} catch {
preconditionFailure("Gini couldn't remove the `userAccessToken` from Keychain.")
}
}

private func removeClientAccessToken() {
// Remove current clientAccessToken from SessionManager
clientAccessToken = nil

// removing `clientAccessToken` from Keychain is part of the old implementation where it was saved in Keychain
do {
try KeychainStore().remove(service: .auth, key: .clientAccessToken)
} catch {
preconditionFailure("Gini couldn't remove the `clientAccessToken` from Keychain.")
}
}

private func removeCurrentUserInfo() {
removeUserAccessToken()
removeClientAccessToken()

do {
try self.keyStore.remove(service: .auth, key: .userEmail)
try self.keyStore.remove(service: .auth, key: .userPassword)
} catch {
preconditionFailure("Gini couldn't remove current user info from the Keychain.")
}
}
}

// MARK: - Fileprivate
Expand All @@ -80,7 +128,10 @@ fileprivate extension SessionManager {
func createUser(completion: @escaping CompletionResult<User>) {
fetchClientAccessToken { result in
switch result {
case .success:
case .success(let token):

self.clientAccessToken = token.accessToken

let domain = self.keyStore.fetch(service: .auth, key: .clientDomain) ?? "no-domain-specified"
let user = AuthHelper.generateUser(with: domain)

Expand All @@ -92,18 +143,8 @@ fileprivate extension SessionManager {
self.data(resource: resource) { result in
switch result {
case .success:
do {
try self.keyStore.save(item: KeychainManagerItem(key: .userEmail,
value: user.email,
service: .auth))
try self.keyStore.save(item: KeychainManagerItem(key: .userPassword,
value: user.password,
service: .auth))
completion(.success((user)))
} catch {
preconditionFailure("Gini couldn't safely save the user credentials in the Keychain. " +
"Enable the 'Keychain Sharing' entitlement in your app")
}
self.storeUserCredentials(for: user,
completion: completion)
case .failure(let error):
completion(.failure(error))
}
Expand All @@ -128,26 +169,26 @@ fileprivate extension SessionManager {
data(resource: resource, completion: completion)
}

func fetchClientAccessToken(completion: @escaping CompletionResult<Void>) {
func fetchClientAccessToken(completion: @escaping CompletionResult<Token>) {
let resource = UserResource<Token>(method: .token(grantType: .clientCredentials),
userDomain: self.userDomain,
httpMethod: .get)
data(resource: resource) { [weak self] result in
guard let self = self else { return }
switch result {
case .success(let token):
do {
try self.keyStore.save(item: KeychainManagerItem(key: .clientAccessToken,
value: token.accessToken,
service: .auth))
completion(.success(()))
} catch {
preconditionFailure("Gini couldn't safely save the user credentials in the Keychain. " +
"Enable the 'Keychain Sharing' entitlement in your app")
}
case .failure(let error):
completion(.failure(error))
}
data(resource: resource, completion: completion)
}

private func storeUserCredentials(for user: User,
completion: @escaping CompletionResult<User>) {
do {
try self.keyStore.save(item: KeychainManagerItem(key: .userEmail,
value: user.email,
service: .auth))
try self.keyStore.save(item: KeychainManagerItem(key: .userPassword,
value: user.password,
service: .auth))
completion(.success((user)))
} catch {
preconditionFailure("Gini couldn't safely save the user credentials in the Keychain. " +
"Enable the 'Keychain Sharing' entitlement in your app")
}
}
}
19 changes: 17 additions & 2 deletions Sources/GiniBankAPILibrary/Documents/Core/GiniError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ public protocol GiniErrorProtocol {
- requestCancelled: Error indicating that the request was cancelled.
- tooManyRequests: Error indicating that too many requests have been made.
- unauthorized: Error indicating that the request was unauthorized.
- maintenance: Error indicating that the system is under maintenance.
- outage: Error indicating that the service is unavailable due to outage.
- server: Error indicating that the server is unavailable.
- unknown: An unknown error occurred.
*/

Expand All @@ -45,8 +48,12 @@ public enum GiniError: Error, GiniErrorProtocol, Equatable {
case requestCancelled
case tooManyRequests(response: HTTPURLResponse? = nil, data: Data? = nil)
case unauthorized(response: HTTPURLResponse? = nil, data: Data? = nil)
case maintenance
case outage
case server
case unknown(response: HTTPURLResponse? = nil, data: Data? = nil)

case noInternetConnection

public var message: String {
switch self {
case .badRequest:
Expand All @@ -65,6 +72,14 @@ public enum GiniError: Error, GiniErrorProtocol, Equatable {
return "Too many requests"
case .unauthorized:
return "Unauthorized"
case .maintenance:
return "Maintenance is in progress"
case .outage:
return "Service is unavailable"
case .server:
return "An unexpected server error occurred"
case .noInternetConnection:
return "No internet connection"
case .unknown:
return "Unknown"
}
Expand All @@ -78,7 +93,7 @@ public enum GiniError: Error, GiniErrorProtocol, Equatable {
.parseError(_, let response, _),
.tooManyRequests(let response, _),
.unauthorized(let response, _),
.unknown(let response, _):
.unknown(let response, _):
return response
default:
return nil
Expand Down
11 changes: 8 additions & 3 deletions Sources/GiniBankAPILibrary/Documents/Core/KeyStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,19 @@ enum KeychainService: String {
}

enum KeychainKey: String {
case clientAccessToken
case clientDomain
case clientId
case clientSecret
case expirationDate
case userAccessToken
case userEmail
case userPassword

/* `userAccessToken` and `clientAccessToken` should be kept here since in all previous SDK versions
were stored in Keychain, and in this current one we need to check if exist and remove them

`userAccessToken` and `clientAccessToken` should be safely removed from Token object in the next SDK version
*/
case userAccessToken
case clientAccessToken
}

struct KeychainManagerItem {
Expand Down
Loading

0 comments on commit 36a05b2

Please sign in to comment.