55// Created by 강동영 on 1/13/26.
66//
77
8+ import Foundation
89
910public protocol JWTTokenStorageable : Sendable {
1011 func save( accessToken: String , refreshToken: String ? ) throws
@@ -13,10 +14,10 @@ public protocol JWTTokenStorageable: Sendable {
1314 func exists( ) throws -> Bool
1415}
1516
16- public final class JWTokenStorage : KeychainTokenStorage , JWTTokenStorageable {
17+ public final class JWTokenStorage : /* KeychainTokenStorage,*/ JWTTokenStorageable {
1718 private let service : String
1819
19- public override init ( service: String = HambugKeychainKey . serviceID) {
20+ public init ( service: String = HambugKeychainKey . serviceID) {
2021 self . service = service
2122 }
2223
@@ -44,3 +45,130 @@ public final class JWTokenStorage: KeychainTokenStorage, JWTTokenStorageable {
4445 try delete ( . refreshToken)
4546 }
4647}
48+
49+ extension JWTokenStorage {
50+
51+ func set( _ value: String , for key: HambugKeychainKey ) throws {
52+ let data = value. data ( using: . utf8) !
53+ let query : [ String : Any ] = [
54+ kSecClass as String : kSecClassGenericPassword,
55+ kSecAttrService as String : service,
56+ kSecAttrAccount as String : key. toString,
57+ kSecValueData as String : data
58+ ]
59+
60+ let status = SecItemAdd ( query as CFDictionary , nil )
61+
62+ do {
63+ try handleError ( status)
64+ } catch KeychainError . duplicateItem {
65+ // 중복 된 아이템이 있다면 업데이트 수행
66+ try updateExisting ( data, for: key)
67+ }
68+ }
69+
70+ func updateExisting( _ data: Data , for key: HambugKeychainKey ) throws {
71+ let updateQuery : [ String : Any ] = [
72+ kSecClass as String : kSecClassGenericPassword,
73+ kSecAttrService as String : service,
74+ kSecAttrAccount as String : key. toString
75+ ]
76+
77+ let attributes : [ String : Any ] = [
78+ kSecValueData as String : data
79+ ]
80+
81+ let updateStatus = SecItemUpdate ( updateQuery as CFDictionary , attributes as CFDictionary )
82+
83+ try handleError ( updateStatus)
84+ }
85+
86+ func string( for key: HambugKeychainKey ) throws -> String ? {
87+ let query : [ String : Any ] = [
88+ kSecClass as String : kSecClassGenericPassword,
89+ kSecAttrService as String : service,
90+ kSecAttrAccount as String : key. toString,
91+ kSecReturnData as String : true ,
92+ kSecMatchLimit as String : kSecMatchLimitOne
93+ ]
94+
95+ var item : CFTypeRef ?
96+ let status = SecItemCopyMatching ( query as CFDictionary , & item)
97+
98+ try handleError ( status)
99+
100+ guard let data = item as? Data ,
101+ let string = String ( data: data, encoding: . utf8)
102+ else {
103+ throw KeychainError . unexpectedPasswordData
104+ }
105+ return string
106+ }
107+
108+ func delete( _ key: HambugKeychainKey ) throws {
109+ let query : [ String : Any ] = [
110+ kSecClass as String : kSecClassGenericPassword,
111+ kSecAttrService as String : service,
112+ kSecAttrAccount as String : key. toString
113+ ]
114+
115+ let status = SecItemDelete ( query as CFDictionary )
116+
117+ try handleError ( status)
118+ }
119+
120+ func contains( _ key: HambugKeychainKey ) throws -> Bool {
121+ let query : [ String : Any ] = [
122+ kSecClass as String : kSecClassGenericPassword,
123+ kSecAttrService as String : service,
124+ kSecAttrAccount as String : key. toString,
125+ ]
126+
127+ return SecItemCopyMatching ( query as CFDictionary , nil ) == errSecSuccess
128+ }
129+
130+ func handleError( _ status: OSStatus ) throws {
131+ switch status {
132+ case errSecSuccess:
133+ return
134+ case errSecDuplicateItem:
135+ throw KeychainError . duplicateItem
136+ case errSecItemNotFound:
137+ throw KeychainError . itemNotFound
138+ default :
139+ throw KeychainError . unexpected ( status)
140+ }
141+ }
142+ }
143+
144+ //public final class JWTokenStorage: KeychainTokenStorage, JWTTokenStorageable {
145+ // private let service: String
146+ //
147+ // public override init(service: String = HambugKeychainKey.serviceID) {
148+ // self.service = service
149+ // }
150+ //
151+ // public func save(accessToken: String, refreshToken: String?) throws {
152+ // try set(accessToken, for: .accessToken)
153+ //
154+ // if let refresh = refreshToken {
155+ // try set(refresh, for: .refreshToken)
156+ // }
157+ // }
158+ //
159+ // public func load() -> (accessToken: String?, refreshToken: String?) {
160+ // let access = try? string(for: .accessToken)
161+ // let refresh = try? string(for: .refreshToken)
162+ //
163+ // return (access, refresh)
164+ // }
165+ //
166+ // public func exists() throws -> Bool {
167+ // try contains(.accessToken)
168+ // }
169+ //
170+ // public func clear() throws {
171+ // try delete(.accessToken)
172+ // try delete(.refreshToken)
173+ // }
174+ //}
0 commit comments