Skip to content

Commit

Permalink
Merge branch 'develop' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
sagishm committed Sep 18, 2022
2 parents c48f456 + cbce224 commit 41c3ff4
Show file tree
Hide file tree
Showing 23 changed files with 967 additions and 70 deletions.
4 changes: 2 additions & 2 deletions Gigya.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |spec|
spec.name = 'Gigya'
spec.version = '1.3.2'
spec.version = '1.4.0'
spec.license = 'Apache 2.0'
spec.homepage = 'https://developers.gigya.com/display/GD/Swift+SDK'
spec.author = 'Gigya SAP'
Expand All @@ -10,7 +10,7 @@ Pod::Spec.new do |spec|
your Swift application
DESC

spec.source = { :git => 'https://github.com/SAP/gigya-swift-sdk.git', :tag => 'core/v1.3.2' }
spec.source = { :git => 'https://github.com/SAP/gigya-swift-sdk.git', :tag => 'core/v1.4.0' }
spec.module_name = 'Gigya'
spec.swift_version = '5.3'

Expand Down
2 changes: 2 additions & 0 deletions GigyaAuth/GigyaAuth.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,7 @@
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_BITCODE = YES;
EXCLUDED_ARCHS = i386;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
Expand Down Expand Up @@ -432,6 +433,7 @@
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_BITCODE = YES;
EXCLUDED_ARCHS = i386;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
Expand Down
3 changes: 2 additions & 1 deletion GigyaNss/GigyaNss.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
E70F8BED2405215C00D52208 /* ActionFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = E70F8BEC2405215C00D52208 /* ActionFactory.swift */; };
E70F8BEF24053ABA00D52208 /* RegisterAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = E70F8BEE24053AB900D52208 /* RegisterAction.swift */; };
E70F8BF124053B5800D52208 /* LoginAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = E70F8BF024053B5800D52208 /* LoginAction.swift */; };
E710952E288EC2E70062DCAD /* Flutter.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7E119B2266CD5C3006EE19C /* Flutter.xcframework */; };
E719C6AF24225216002F8C36 /* LogChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E719C6AE24225216002F8C36 /* LogChannel.swift */; };
E719C6B124226139002F8C36 /* SetAccountAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = E719C6B024226139002F8C36 /* SetAccountAction.swift */; };
E71CB3B22554029000D63726 /* JsEvaluatorHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = E71CB3B12554029000D63726 /* JsEvaluatorHelper.swift */; };
Expand Down Expand Up @@ -153,7 +154,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
E732A81828AA2CE00035DDEA /* Flutter.xcframework in Frameworks */,
E710952E288EC2E70062DCAD /* Flutter.xcframework in Frameworks */,
E76421A325D51FFD00CDED4C /* GigyaAuth.framework in Frameworks */,
E7288CA32455CFD800AD99CA /* Gigya.framework in Frameworks */,
);
Expand Down
142 changes: 85 additions & 57 deletions GigyaSwift.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion GigyaSwift/Gigya/GigyaCore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,24 @@ public final class GigyaCore<T: GigyaAccountProtocol>: GigyaInstanceProtocol {
*/
public let biometric: BiometricServiceProtocol

/**
WebAuthn (FIDO2).
- returns: `WebAuthnService` service
*/
public let webAuthn: WebAuthnService<T>

// MARK: - Initialize

internal init(config: GigyaConfig, persistenceService: PersistenceService, businessApiService: BusinessApiServiceProtocol, sessionService: SessionServiceProtocol, interruptionResolver: InterruptionResolverFactoryProtocol, biometric: BiometricServiceProtocol, plistFactory: PlistConfigFactory, sessionVerificationService: SessionVerificationServiceProtocol, container: IOCContainer) {
internal init(config: GigyaConfig, persistenceService: PersistenceService, businessApiService: BusinessApiServiceProtocol, sessionService: SessionServiceProtocol, interruptionResolver: InterruptionResolverFactoryProtocol, biometric: BiometricServiceProtocol, plistFactory: PlistConfigFactory, sessionVerificationService: SessionVerificationServiceProtocol, webAuthn: WebAuthnService<T>, container: IOCContainer) {
self.config = config
self.persistenceService = persistenceService
self.businessApiService = businessApiService
self.sessionService = sessionService
self.interruptionResolver = interruptionResolver
self.biometric = biometric
self.container = container
self.webAuthn = webAuthn
self.sessionVerificationService = sessionVerificationService

// load plist and make init
Expand Down
2 changes: 1 addition & 1 deletion GigyaSwift/Global/Biometric/BiometricService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ final class BiometricService: BiometricServiceProtocol, BiometricServiceInternal
*/

var isLocked: Bool {
return persistenceService.biometricLocked ?? false
return (isOptIn && !sessionService.isValidSession())
}

init(config: GigyaConfig, persistenceService: PersistenceService, sessionService: SessionServiceProtocol) {
Expand Down
24 changes: 24 additions & 0 deletions GigyaSwift/Global/PersistenceService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,20 @@ public final class PersistenceService {
return UserDefaults.standard.string(forKey: InternalConfig.Storage.pushKey)
}
}

public var webAuthnlist: [GigyaWebAuthnCredential] {
get {
if let data = UserDefaults.standard.object(forKey: InternalConfig.Storage.webAuthn) as? Data {
do {
return try PropertyListDecoder().decode([GigyaWebAuthnCredential].self, from: data)
} catch {
return []
}
}

return []
}
}

// save gmid, ucid to userDefaults
internal func save(ids: InitSdkIdsModel) {
Expand All @@ -77,4 +91,14 @@ public final class PersistenceService {
internal func setPushKey(to string: String) {
UserDefaults.standard.setValue(string, forKey: InternalConfig.Storage.pushKey)
}

func addWebAuthnKey(model: GigyaWebAuthnCredential) {
var list = webAuthnlist
list.append(model)
UserDefaults.standard.set(try? PropertyListEncoder().encode(list), forKey: InternalConfig.Storage.webAuthn)
}

internal func removeAllWebAuthnKeys() {
UserDefaults.standard.removeObject(forKey: InternalConfig.Storage.webAuthn)
}
}
2 changes: 1 addition & 1 deletion GigyaSwift/Global/Plugins/GigyaWebBridge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ public class GigyaWebBridge<T: GigyaAccountProtocol>: NSObject, WKScriptMessageH
guard let self = self else { return }
switch result {
case .success(let data):
GigyaLogger.log(with: self, message: "sendRequest: success")
GigyaLogger.log(with: self, message: "sendRequest: success = \(apiMethod)")
// Mapping AnyCodable values. Otherwise we will crash in the JSON dictionary conversion.
let mapped: [String: Any] = data.mapValues { value in return value.value }

Expand Down
1 change: 0 additions & 1 deletion GigyaSwift/Global/Resolvers/LinkAccountsResolver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,5 @@ final public class LinkAccountsResolver<T: GigyaAccountProtocol>: BaseResolver {
businessDelegate?.callSociallogin(provider: provider, viewController: viewController, params: params, dataType: T.self, completion: self.completion)

GigyaLogger.log(with: self, message: "[linkToSocial] - data: \(params), provider: \(provider.rawValue)")

}
}
4 changes: 2 additions & 2 deletions GigyaSwift/Global/Utils/Decode+EncodeUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@

import Foundation

final class DecodeEncodeUtils {
public final class DecodeEncodeUtils {

static func decode<T>(fromType: T.Type, data: Data) throws -> T where T: Codable {
public static func decode<T>(fromType: T.Type, data: Data) throws -> T where T: Codable {
do {
let decodedObject = try JSONDecoder().decode(fromType, from: data)
return decodedObject
Expand Down
18 changes: 18 additions & 0 deletions GigyaSwift/Global/Utils/GeneralUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,21 @@ extension Dictionary {
return self.reduce("") { "\($0)\($1.0)=\("\($1.1)".addingPercentEncoding(withAllowedCharacters: urlAllowed) ?? "")&" }
}
}

extension String {
func decodeBase64Url() -> Data? {
var base64 = self
.replacingOccurrences(of: "-", with: "+")
.replacingOccurrences(of: "_", with: "/")
if base64.count % 4 != 0 {
base64.append(String(repeating: "=", count: 4 - base64.count % 4))
}
return Data(base64Encoded: base64)
}
}

extension Data {
func toBase64Url() -> String {
return self.base64EncodedString().replacingOccurrences(of: "+", with: "-").replacingOccurrences(of: "/", with: "_").replacingOccurrences(of: "=", with: "")
}
}
26 changes: 26 additions & 0 deletions GigyaSwift/Global/Utils/GigyaIOCContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ final class GigyaIOCContainer<T: GigyaAccountProtocol>: GigyaContainerProtocol {
let persistenceService = resolver.resolve(PersistenceService.self)
let container = resolver.resolve(IOCContainer.self)
let sessionVerificationService = resolver.resolve(SessionVerificationServiceProtocol.self)
let webAuthn = resolver.resolve(WebAuthnService<T>.self)

return GigyaCore(config: config!,
persistenceService: persistenceService!,
Expand All @@ -208,6 +209,7 @@ final class GigyaIOCContainer<T: GigyaAccountProtocol>: GigyaContainerProtocol {
biometric: biometricService!,
plistFactory: plistFactory!,
sessionVerificationService: sessionVerificationService!,
webAuthn: webAuthn!,
container: container!)
}

Expand All @@ -220,5 +222,29 @@ final class GigyaIOCContainer<T: GigyaAccountProtocol>: GigyaContainerProtocol {

return businessService as! BusinessApiDelegate
}

container.register(service: WebAuthnService<T>.self) { resolver in
let busnessApi = resolver.resolve(BusinessApiServiceProtocol.self)
let webAuthnDeviceIntegration = resolver.resolve(WebAuthnDeviceIntegration.self)
let oauthService = resolver.resolve(OauthService.self)
let attestationUtils = resolver.resolve(WebAuthnAttestationUtils.self)
let persistenceService = resolver.resolve(PersistenceService.self)

return WebAuthnService(businessApiService: busnessApi!, webAuthnDeviceIntegration: webAuthnDeviceIntegration!, oauthService: oauthService!, attestationUtils: attestationUtils!, persistenceService: persistenceService!)
}

container.register(service: WebAuthnAttestationUtils.self) { resolver in
return WebAuthnAttestationUtils()
}

container.register(service: WebAuthnDeviceIntegration.self) { resolver in
return WebAuthnDeviceIntegration()
}

container.register(service: OauthService.self) { resolver in
let busnessApi = resolver.resolve(BusinessApiServiceProtocol.self)

return OauthService(businessApiService: busnessApi!)
}
}
}
68 changes: 68 additions & 0 deletions GigyaSwift/Global/WebAuthn/OauthService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// OauthService.swift
// Gigya
//
// Created by Sagi Shmuel on 05/07/2022.
// Copyright © 2022 Gigya. All rights reserved.
//

import Foundation

class OauthService {
let businessApiService: BusinessApiServiceProtocol

init(businessApiService: BusinessApiServiceProtocol) {
self.businessApiService = businessApiService
}

func connect(token: String, completion: @escaping (GigyaApiResult<GigyaDictionary>) -> Void) {
var model = ApiRequestModel(method: GigyaDefinitions.Oauth.connect)
model.headers = ["Authorization": "Bearer \(token)"]
businessApiService.apiService.send(model: model, responseType: GigyaDictionary.self) { result in
completion(result)
}
}

@available(iOS 13.0.0, *)
func authorize<T: Codable>(token: String) async -> GigyaLoginResult<T> {
return await withCheckedContinuation { [weak self] continuation in
guard let self = self else { return }

var model = ApiRequestModel(method: GigyaDefinitions.Oauth.authorize, params: ["response_type": "code"])
model.headers = ["Authorization": "Bearer \(token)"]

self.businessApiService.apiService.send(model: model, responseType: GigyaDictionary.self) { result in
switch result {
case .success(data: let data):
self.token(continuation: continuation, code: data["code"]?.value as! String)
case .failure(let error):
continuation.resume(returning: GigyaLoginResult.failure(.init(error: error)))
}
}
}

}

@available(iOS 13.0.0, *)
private func token<T: Codable>(continuation: CheckedContinuation<GigyaLoginResult<T>, Never>, code: String) {
self.businessApiService.send(dataType: T.self, api: GigyaDefinitions.Oauth.token, params: ["grant_type": "authorization_code", "code": code]) { [weak self] res in
guard let self = self else { return }

switch res {
case .success(data: _):

self.businessApiService.getAccount(params: [:], clearAccount: true, dataType: T.self) { result in
switch result {
case .success(data: let data):
continuation.resume(returning: .success(data: data))
case .failure(let error):
continuation.resume(returning: .failure(LoginApiError.init(error: error)))
}
}
case .failure(let error):
continuation.resume(returning: .failure(LoginApiError.init(error: error)))
}
}
}
}

84 changes: 84 additions & 0 deletions GigyaSwift/Global/WebAuthn/WebAuthnAttestationUtils.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//
// WebAuthnAttestationUtils.swift
// Gigya
//
// Created by Sagi Shmuel on 24/07/2022.
// Copyright © 2022 Gigya. All rights reserved.
//

import AuthenticationServices

struct WebAuthnAttestationUtils {
@available(iOS 16.0, *)
func makeRegisterData(object: ASAuthorizationPlatformPublicKeyCredentialRegistration) -> [String: Any] {
let response: [String: String] = [
"attestationObject": object.rawAttestationObject!.toBase64Url(),
"clientDataJSON": object.rawClientDataJSON.toBase64Url()
]

let attestation: [String: Any] = [
"id": object.credentialID.toBase64Url(),
"rawId": object.credentialID.toBase64Url(),
"type": "public-key",
"response": response
]

return attestation
}

@available(iOS 16.0, *)
func makeRegisterData(object: ASAuthorizationSecurityKeyPublicKeyCredentialRegistration) -> [String: Any] {
let response: [String: String] = [
"attestationObject": object.rawAttestationObject!.toBase64Url(),
"clientDataJSON": object.rawClientDataJSON.toBase64Url()
]

let attestation: [String: Any] = [
"id": object.credentialID.toBase64Url(),
"rawId": object.credentialID.toBase64Url(),
"type": "public-key",
"response": response
]

return attestation
}


@available(iOS 16.0, *)
func makeLoginData(object: ASAuthorizationPlatformPublicKeyCredentialAssertion) -> [String: Any] {
let response: [String: Any?] = [
"authenticatorData": object.rawAuthenticatorData.toBase64Url(),
"clientDataJSON": object.rawClientDataJSON.toBase64Url(),
"signature": object.signature.toBase64Url(),
"userHandle": object.userID.toBase64Url()
]

let attestation: [String: Any] = [
"id": object.credentialID.toBase64Url(),
"rawId": object.credentialID.toBase64Url(),
"type": "public-key",
"response": response
]

return attestation
}

@available(iOS 16.0, *)
func makeSecurityLoginData(object: ASAuthorizationSecurityKeyPublicKeyCredentialAssertion) -> [String: Any] {
let response: [String: Any] = [
"authenticatorData": object.rawAuthenticatorData.toBase64Url(),
"clientDataJSON": object.rawClientDataJSON.toBase64Url(),
"signature": object.signature.toBase64Url(),
"userHandle": NSNull()
]

let attestation: [String: Any] = [
"id": object.credentialID.toBase64Url(),
"rawId": object.credentialID.toBase64Url(),
"type": "public-key",
"response": response
]

return attestation
}
}
Loading

0 comments on commit 41c3ff4

Please sign in to comment.