Skip to content

Commit

Permalink
Merge pull request #572 from pennlabs/notifications/token-manager
Browse files Browse the repository at this point in the history
Support new notification token protocol
  • Loading branch information
anli5005 authored Nov 18, 2024
2 parents 70b61bc + 021cd7f commit 021674b
Show file tree
Hide file tree
Showing 9 changed files with 250 additions and 118 deletions.
12 changes: 8 additions & 4 deletions PennMobile.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@
895A1AB82CB98F5000E161AE /* ScrollableGraphView in Frameworks */ = {isa = PBXBuildFile; productRef = 895A1AB72CB98F5000E161AE /* ScrollableGraphView */; };
895A1ABB2CB98F7100E161AE /* SCLAlertView in Frameworks */ = {isa = PBXBuildFile; productRef = 895A1ABA2CB98F7100E161AE /* SCLAlertView */; };
898DB4912B2E7AA20027CC8F /* PennForms in Frameworks */ = {isa = PBXBuildFile; productRef = 898DB4902B2E7AA20027CC8F /* PennForms */; };
89DF63072CEB9BBB00C4A015 /* NotificationDeviceTokenManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89DF63062CEB9BB400C4A015 /* NotificationDeviceTokenManager.swift */; };
89EA262E290F9411008F26CF /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 89EA262D290F9411008F26CF /* Intents.intentdefinition */; };
89EA262F290F958B008F26CF /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 89EA262D290F9411008F26CF /* Intents.intentdefinition */; };
F213CCE223C3EE3E000AD90F /* SwiftSoup in Frameworks */ = {isa = PBXBuildFile; productRef = F213CCE123C3EE3E000AD90F /* SwiftSoup */; };
Expand Down Expand Up @@ -807,6 +808,7 @@
8932693328FC75A5003D4BF9 /* WidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = WidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
8932693428FC75A5003D4BF9 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
8932693628FC75A5003D4BF9 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
89DF63062CEB9BB400C4A015 /* NotificationDeviceTokenManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationDeviceTokenManager.swift; sourceTree = "<group>"; };
89EA262D290F9411008F26CF /* Intents.intentdefinition */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; path = Intents.intentdefinition; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -1750,6 +1752,7 @@
42632C4C2CB9C77B0028CC31 /* NotificationRequestable.swift */,
42632C4D2CB9C77B0028CC31 /* NotificationsTableViewCell.swift */,
42632C4E2CB9C77B0028CC31 /* NotificationsTableViewController.swift */,
89DF63062CEB9BB400C4A015 /* NotificationDeviceTokenManager.swift */,
);
path = Notifications;
sourceTree = "<group>";
Expand Down Expand Up @@ -2454,6 +2457,7 @@
42632D072CB9C77B0028CC31 /* GSRTabController.swift in Sources */,
42632D082CB9C77B0028CC31 /* GSRAPIResponse.swift in Sources */,
42632D092CB9C77B0028CC31 /* GSRBooking.swift in Sources */,
89DF63072CEB9BBB00C4A015 /* NotificationDeviceTokenManager.swift in Sources */,
42632D0A2CB9C77B0028CC31 /* GSRDateHandler.swift in Sources */,
42632D0B2CB9C77B0028CC31 /* GSRGroup.swift in Sources */,
42632D0C2CB9C77B0028CC31 /* GSRGroupUser.swift in Sources */,
Expand Down Expand Up @@ -2850,7 +2854,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = navigation;
CF_BUNDLE_SHORT_VERSION_STRING = 8.0.6;
CF_BUNDLE_SHORT_VERSION_STRING = 8.0.7;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = PennMobile/PennMobile.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
Expand Down Expand Up @@ -2891,7 +2895,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = navigation;
CF_BUNDLE_SHORT_VERSION_STRING = 8.0.6;
CF_BUNDLE_SHORT_VERSION_STRING = 8.0.7;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = PennMobile/PennMobile.entitlements;
CODE_SIGN_IDENTITY = "iPhone Distribution";
Expand Down Expand Up @@ -3023,7 +3027,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = navigation;
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CF_BUNDLE_SHORT_VERSION_STRING = 8.0.6;
CF_BUNDLE_SHORT_VERSION_STRING = 8.0.7;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
Expand Down Expand Up @@ -3066,7 +3070,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = navigation;
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CF_BUNDLE_SHORT_VERSION_STRING = 8.0.6;
CF_BUNDLE_SHORT_VERSION_STRING = 8.0.7;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,6 @@
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
<EnvironmentVariables>
<EnvironmentVariable
key = "OS_ACTIVITY_MODE"
value = "disable"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
Expand Down
38 changes: 24 additions & 14 deletions PennMobile/Auth/OAuth2NetworkManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,6 @@ struct AccessToken: Codable {
let expiration: Date
}

extension URLRequest {
// Sets the appropriate header field given an access token
// NOTE: Should ONLY be used for requests to Labs servers. Otherwise, access token will be compromised.
init(url: URL, accessToken: AccessToken) {
self.init(url: url)
// Authorization headers are restricted on iOS and not supposed to be set. They can be removed at any time.
// Thus, we et an X-Authorization header to carry the bearer token in addition to the regular Authorization header.
// For more info: see https://developer.apple.com/documentation/foundation/nsurlrequest#1776617
setValue("Bearer \(accessToken.value)", forHTTPHeaderField: "Authorization")
setValue("Bearer \(accessToken.value)", forHTTPHeaderField: "X-Authorization")
}
}

enum OAuth2State {
case unknown
case unauthenticated
Expand Down Expand Up @@ -56,7 +43,7 @@ enum OAuth2State {
}
}

private static let logger = Logger(subsystem: "org.pennlabs.PennMobile", category: "OAuth2NetworkManager")
private static let logger = Logger(category: "OAuth2NetworkManager")

private struct TokenResponse: Decodable {
var expiresIn: Int
Expand Down Expand Up @@ -300,6 +287,29 @@ extension String {
}
}

// MARK: - Utilities

extension URLRequest {
// Sets the appropriate header field given an access token
// NOTE: Should ONLY be used for requests to Labs servers. Otherwise, access token will be compromised.
init(url: URL, accessToken: AccessToken) {
self.init(url: url)
// Authorization headers are restricted on iOS and not supposed to be set. They can be removed at any time.
// Thus, we et an X-Authorization header to carry the bearer token in addition to the regular Authorization header.
// For more info: see https://developer.apple.com/documentation/foundation/nsurlrequest#1776617
setValue("Bearer \(accessToken.value)", forHTTPHeaderField: "Authorization")
setValue("Bearer \(accessToken.value)", forHTTPHeaderField: "X-Authorization")
}

init(authenticatedUrl: URL, authNetworkManager: OAuth2NetworkManager = .shared) async throws {
guard let token = try await authNetworkManager.getAccessToken() else {
throw NetworkingError.authenticationError
}

self.init(url: authenticatedUrl, accessToken: token)
}
}

// MARK: - Legacy Support

extension OAuth2NetworkManager {
Expand Down
81 changes: 0 additions & 81 deletions PennMobile/General/Networking + Analytics/UserDBManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -465,87 +465,6 @@ extension UserDBManager {
}
}

// MARK: - Push Notifications
extension UserDBManager {
// Gets the notification token information using the access token.
func getNotificationId(_ completion: @escaping (_ result: Result<[GetNotificationID], NetworkingError>) -> Void) {
OAuth2NetworkManager.instance.getAccessToken { (token) in
guard let token = token else {
completion(.failure(.authenticationError))
return
}
let url = URL(string: "https://pennmobile.org/api/user/notifications/tokens/")!
var params: [String: Any] = [
"dev": false
]

#if DEBUG
params["dev"] = true
#endif

let request = URLRequest(url: url, accessToken: token)
let task = URLSession.shared.dataTask(with: request) { data, _, _ in
guard let data = data else {
completion(.failure(.serverError))
return
}

let decoder = JSONDecoder()
if let response = try?
decoder.decode([GetNotificationID].self, from: data) {
completion(.success(response))
} else {
completion(.failure(.parsingError))
}
}
task.resume()
}
}

// Updates device token.
func savePushNotificationDeviceToken(deviceToken: String, notifId: Int, _ completion: (() -> Void)? = nil) {
Task {
defer { completion?() }

struct DeviceTokenRequestBody: Encodable {
var kind: String
var token: String
var dev: Bool
}

guard let url = URL(string: "https://pennmobile.org/api/user/notifications/tokens/\(notifId)/") else {
return
}

var body = DeviceTokenRequestBody(kind: "IOS", token: deviceToken, dev: false)

#if DEBUG
body.dev = true
#endif

guard let token = try? await OAuth2NetworkManager.instance.getAccessToken() else {
return
}

var request = URLRequest(url: url, accessToken: token)
request.httpMethod = "PUT"

// If serializing a simple JSON object fails something is really wrong
request.httpBody = try! JSONEncoder().encode(body)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")

_ = try? await URLSession.shared.data(for: request)
}
}

func clearPushNotificationDeviceToken(_ completion: (() -> Void)? = nil) {
let url = "\(baseUrl)/notifications/register"
makePostRequestWithAccessToken(url: url, params: [:]) { (_, _, _) in
completion?()
}
}
}

// MARK: - Anonymized Token Registration
extension UserDBManager {
/// Updates the anonymization keys in case either of them changed. The only key that may change is the pennkey-password.
Expand Down
15 changes: 15 additions & 0 deletions PennMobile/Login/AuthManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,18 @@ class AuthManager: ObservableObject {
AuthManager.clearAccountData()
}
}

extension AuthState: CustomDebugStringConvertible {
var debugDescription: String {
switch self {
case .notDetermined:
"Not determined"
case .loggedOut:
"Logged out"
case .guest:
"Guest"
case .loggedIn(let account):
"Logged in as \(account.username)"
}
}
}
Loading

0 comments on commit 021674b

Please sign in to comment.