修改KBAuthSession主程序添加token extension没有拿到的情况

This commit is contained in:
2025-10-31 16:50:15 +08:00
parent 90c1e7ff6c
commit ffea9d2022
9 changed files with 642 additions and 1 deletions

View File

@@ -0,0 +1,211 @@
//
// KBAuthManager.m
//
//
// - 使 service/account KBAuthSession
// - kSecAttrAccessGroup Keychain Sharing 访 App
// - / Darwin 便
//
#import "KBAuthManager.h"
#import <Security/Security.h>
#import "KBConfig.h" // 访 KBConfig.h
NSString * const kKBDarwinAuthChanged = @"com.keyBoardst.auth.changed";
NSNotificationName const KBAuthChangedNotification = @"KBAuthChangedNotification";
static NSString * const kKBKCService = @"com.keyBoardst.auth"; // service
static NSString * const kKBKCAccount = @"session"; // account
// Keychain Sharing 访 target entitlements
// Capabilities Keychain Sharing
// $(AppIdentifierPrefix)com.keyBoardst.shared
// TN6HHV45BB.com.keyBoardst.shared
#ifndef KB_KEYCHAIN_ACCESS_GROUP
#define KB_KEYCHAIN_ACCESS_GROUP @"TN6HHV45BB.com.keyBoardst.shared"
#endif
// <=
static const NSTimeInterval kKBExpiryGrace = 5.0; //
@implementation KBAuthSession
+ (BOOL)supportsSecureCoding { return YES; }
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:self.accessToken forKey:@"accessToken"];
[coder encodeObject:self.refreshToken forKey:@"refreshToken"];
[coder encodeObject:self.expiryDate forKey:@"expiryDate"];
[coder encodeObject:self.userIdentifier forKey:@"userIdentifier"];
}
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super init]) {
_accessToken = [coder decodeObjectOfClass:NSString.class forKey:@"accessToken"];
_refreshToken = [coder decodeObjectOfClass:NSString.class forKey:@"refreshToken"];
_expiryDate = [coder decodeObjectOfClass:NSDate.class forKey:@"expiryDate"];
_userIdentifier = [coder decodeObjectOfClass:NSString.class forKey:@"userIdentifier"];
}
return self;
}
@end
@interface KBAuthManager ()
@property (atomic, strong, readwrite, nullable) KBAuthSession *current;
@end
@implementation KBAuthManager
+ (instancetype)shared {
static KBAuthManager *m; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ m = [KBAuthManager new]; });
return m;
}
#if DEBUG
static inline void KBLog(NSString *fmt, ...) {
va_list args; va_start(args, fmt);
NSString *msg = [[NSString alloc] initWithFormat:fmt arguments:args];
va_end(args);
NSLog(@"[KBAuth] %@", msg);
}
#endif
- (instancetype)init {
if (self = [super init]) {
[self reloadFromKeychain];
// Darwin App
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
(__bridge const void *)(self),
KBAuthDarwinCallback,
(__bridge CFStringRef)kKBDarwinAuthChanged,
NULL,
CFNotificationSuspensionBehaviorDeliverImmediately);
}
return self;
}
static void KBAuthDarwinCallback(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) {
KBAuthManager *self = (__bridge KBAuthManager *)observer;
[self reloadFromKeychain];
}
- (void)dealloc {
CFNotificationCenterRemoveObserver(CFNotificationCenterGetDarwinNotifyCenter(), (__bridge const void *)(self), (__bridge CFStringRef)kKBDarwinAuthChanged, NULL);
}
- (BOOL)isLoggedIn {
KBAuthSession *s = self.current;
if (s.accessToken.length == 0) return NO;
if (!s.expiryDate) return YES; // token
return ([s.expiryDate timeIntervalSinceNow] > kKBExpiryGrace);
}
#pragma mark - Public
- (void)reloadFromKeychain {
NSData *data = [self keychainRead];
KBAuthSession *session = nil;
if (data.length > 0) {
@try {
session = [NSKeyedUnarchiver unarchivedObjectOfClass:KBAuthSession.class fromData:data error:NULL];
} @catch (__unused NSException *e) { session = nil; }
}
self.current = session;
[[NSNotificationCenter defaultCenter] postNotificationName:KBAuthChangedNotification object:nil]; //
}
- (BOOL)saveAccessToken:(NSString *)accessToken
refreshToken:(NSString *)refreshToken
expiryDate:(NSDate *)expiryDate
userIdentifier:(NSString *)userIdentifier {
KBAuthSession *s = [KBAuthSession new];
s.accessToken = accessToken ?: @"";
s.refreshToken = refreshToken;
s.expiryDate = expiryDate;
s.userIdentifier = userIdentifier;
NSError *err = nil;
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:s requiringSecureCoding:YES error:&err];
if (err || data.length == 0) return NO;
BOOL ok = [self keychainWrite:data];
if (ok) {
self.current = s;
//
[[NSNotificationCenter defaultCenter] postNotificationName:KBAuthChangedNotification object:nil];
// App <->
CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), (__bridge CFStringRef)kKBDarwinAuthChanged, NULL, NULL, true);
}
return ok;
}
- (void)signOut {
[self keychainDelete];
self.current = nil;
[[NSNotificationCenter defaultCenter] postNotificationName:KBAuthChangedNotification object:nil];
CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), (__bridge CFStringRef)kKBDarwinAuthChanged, NULL, NULL, true);
}
- (NSDictionary<NSString *,NSString *> *)authorizationHeader {
NSString *t = self.current.accessToken;
if (t.length == 0) return @{}; //
return @{ @"Authorization": [@"Bearer " stringByAppendingString:t] };
}
#pragma mark - Keychain (shared)
- (NSMutableDictionary *)baseKCQuery {
NSMutableDictionary *q = [@{ (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService: kKBKCService,
(__bridge id)kSecAttrAccount: kKBKCAccount } mutableCopy];
// 访App
q[(__bridge id)kSecAttrAccessGroup] = KB_KEYCHAIN_ACCESS_GROUP;
return q;
}
- (BOOL)keychainWrite:(NSData *)data {
if (!data) return NO;
NSMutableDictionary *query = [self baseKCQuery];
SecItemDelete((__bridge CFDictionaryRef)query);
//
query[(__bridge id)kSecValueData] = data;
// 访
query[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly;
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)query, NULL);
#if DEBUG
if (status != errSecSuccess) {
KBLog(@"SecItemAdd failed status=%ld group=%@", (long)status, KB_KEYCHAIN_ACCESS_GROUP);
} else {
KBLog(@"SecItemAdd ok group=%@", KB_KEYCHAIN_ACCESS_GROUP);
}
#endif
return (status == errSecSuccess);
}
- (NSData *)keychainRead {
NSMutableDictionary *query = [self baseKCQuery];
query[(__bridge id)kSecReturnData] = @YES;
query[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne;
CFTypeRef dataRef = NULL;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &dataRef);
#if DEBUG
if (status != errSecSuccess) {
KBLog(@"SecItemCopyMatching status=%ld group=%@ (item not found or no entitlement)", (long)status, KB_KEYCHAIN_ACCESS_GROUP);
} else {
KBLog(@"SecItemCopyMatching ok group=%@", KB_KEYCHAIN_ACCESS_GROUP);
}
#endif
if (status != errSecSuccess || !dataRef) return nil;
return (__bridge_transfer NSData *)dataRef;
}
- (void)keychainDelete {
NSDictionary *query = [self baseKCQuery];
SecItemDelete((__bridge CFDictionaryRef)query);
}
@end