feat: add JWT Support to MDM and refacture (MESSENGER-6162)

This commit is contained in:
Frank Rotermund
2024-07-25 12:51:43 +02:00
parent bb60e8f85d
commit dc6c9f8fc4
9 changed files with 53 additions and 88 deletions

View File

@@ -173,8 +173,10 @@ class BWIBuildSettings: NSObject {
var bwiHashes = [ "a3f65e35a7476799afe8d80282fb3c45b39dab06d1d8c70dc98e45ab7d8e93a9",
"2fda1a831655c22a5e6096d7cfbff4429fbf27891141e191b46adbf168142a11",
"4f8cbb3fef885f7284d0477d797d7007f0e1ba76221834132752f4d645796e28",
/* "24c2ec541e61e8e68944b96dc45ed5df12f6bdbda283cb0b3a522742aa970256", remove internal test server that is handled by token verification*/
"1be0b314a6c915d4475290522baef5b642db1b6d68937792b8e0eb5b7b0d6666",
/* "24c2ec541e61e8e68944b96dc45ed5df12f6bdbda283cb0b3a522742aa970256",
"1be0b314a6c915d4475290522baef5b642db1b6d68937792b8e0eb5b7b0d6666",
remove internal test server that is handled by token verification*/
"3deb73db8cafcd1d5a59e25e251c35816162e1f6ee67b5d7d011da0e8d6ef931",
"42e57985d61202c2c7dd87d898cef9bdce020877a4c7a8c7cd699f6a28f58c0c",
"e1c3c7cac12bd65bd48de79a2677187d2e768d2769377627534023588b8d7a33",

View File

@@ -978,17 +978,7 @@
if (sender == _submitButton)
{
if (BWIBuildSettings.shared.bwiEnableLoginProtection) {
LoginProtectionService* service = [[LoginProtectionService alloc] init];
service.hashes = BWIBuildSettings.shared.bwiHashes;
if (![service isValid:self->mxRestClient.homeserver]) {
NSError *error = [[NSError alloc] initWithDomain:@"LoginProtectionError" code:0 userInfo:@{
NSLocalizedDescriptionKey: [BWIL10n bwiLoginProtectionErrorMessage:AppInfo.current.displayName]}];
[self onFailureDuringAuthRequest:error];
return;
}
}
// Disable user interaction to prevent multiple requests
self.userInteractionEnabled = NO;

View File

@@ -109,3 +109,4 @@ targets:
- path: Assets/de.lproj/Vector.strings
- path: Assets/de.lproj/Bwi.strings
- path: ../publickeys
type: folder

View File

@@ -95,3 +95,4 @@ targets:
- path: Assets/de.lproj/Vector.strings
- path: Assets/de.lproj/Bwi.strings
- path: ../publickeys
type: folder

View File

@@ -68,6 +68,7 @@ targets:
- path: ../Config/BuM/BWIBuildSettings+BuM.swift
- path: ../bwi/UserAgent/UserAgentService.swift
- path: ../bwi/LoginProtection/LoginProtectionService.swift
- path: ../bwi/TokenVerification/TokenVerificator.swift
- path: ../Config/MDMSettings.swift
- path: ../Riot/Utils/DataProtectionHelper.swift
- path: ../Config/CommonConfiguration.swift

View File

@@ -226,37 +226,15 @@ struct AuthenticationServerSelectionScreen: View {
}
private func isHomeserverAddressValid(_ homeserverAddress: String) async -> Bool {
// bwi #6162 a homeserveraddress is valid when there is either
// a) no homeserver protection (bwm)
// b) tokenized protection and there is a valid token
// c) hashed protection and there is a valid hash (this will be disabled soon)
// d) b) && c) can be combined for now
if !BWIBuildSettings.shared.bwiEnableTokenizedLoginProtection && !BWIBuildSettings.shared.bwiEnableLoginProtection {
return true
}
var validHomeserver = false
if BWIBuildSettings.shared.bwiEnableTokenizedLoginProtection {
let tokenVerificator = ServerTokenVerificator()
let token = await tokenVerificator.fetchToken(baseURL: homeserverAddress)
if let token = token {
validHomeserver = tokenVerificator.verifyToken(baseURL: homeserverAddress, token: token)
}
}
if BWIBuildSettings.shared.bwiEnableLoginProtection && !validHomeserver {
if BWIBuildSettings.shared.bwiEnableLoginProtection || BWIBuildSettings.shared.bwiEnableTokenizedLoginProtection {
let protectionService = LoginProtectionService()
protectionService.hashes = BWIBuildSettings.shared.bwiHashes
validHomeserver = protectionService.isValid(homeserverAddress)
return await protectionService.isValid(homeserverAddress)
}
return validHomeserver
return true
}
/// bwi: jump directly into the iOS settings app to allow camera access

View File

@@ -76,12 +76,12 @@ extension UserDefaults
}
}
private func checkUrlSavety(_ serverUrl: String) -> Bool {
private func checkUrlSavety(_ serverUrl: String) async -> Bool {
if BWIBuildSettings.shared.bwiEnableLoginProtection {
let protectionService = LoginProtectionService()
protectionService.hashes = BWIBuildSettings.shared.bwiHashes
return protectionService.isValid(serverUrl)
return await protectionService.isValid(serverUrl)
} else {
return true
}
@@ -112,11 +112,13 @@ extension UserDefaults
func registerForAppConfig() {
NotificationCenter.default.addObserver(forName: UserDefaults.didChangeNotification, object: nil, queue: OperationQueue.main) { [self] (note) in
handleAppConfig()
Task {
await handleAppConfig()
}
}
}
func handleAppConfig() {
func handleAppConfig() async {
if let dict = UserDefaults.standard.dictionary(forKey: configKey) {
// only compute if serverURL has not changed (this may need to be changed on Adminportal integration
if !isSameConfig(dict: dict) {
@@ -125,22 +127,22 @@ extension UserDefaults
if let serverUrl = dict[serverUrlKey] as? String {
if serverUrl.count == 0 {
config.serverUrl = nil
} else if checkUrlSavety(serverUrl) {
} else if await checkUrlSavety(serverUrl) {
config.serverUrl = serverUrl
}
}
if let contentScannerUrl = dict[contentScannerKey] as? String {
if checkUrlSavety(contentScannerUrl) {
if await checkUrlSavety(contentScannerUrl) {
config.contentScannerUrl = contentScannerUrl
}
}
if let pusherUrl = dict[pusherUrlKey] as? String {
if checkUrlSavety(pusherUrl) {
if await checkUrlSavety(pusherUrl) {
config.pusherUrl = pusherUrl
}
}
if let permalinkUrl = dict[permalinkUrlKey] as? String {
if checkUrlSavety(permalinkUrl) {
if await checkUrlSavety(permalinkUrl) {
config.permalinkUrl = permalinkUrl
}
}

View File

@@ -21,15 +21,36 @@ import CryptoKit
@objcMembers class LoginProtectionService : NSObject {
var hashes: [String]?
@objc func isValid(_ urlString: String) -> Bool {
guard let hashes = hashes else {
return false
@objc func isValid(_ homeserverAddress: String) async -> Bool {
// bwi #6162 a homeserveraddress is valid when there is either
// a) no homeserver protection (bwm)
// b) tokenized protection and there is a valid token
// c) hashed protection and there is a valid hash (this will be disabled soon)
// d) b) && c) can be combined for now
var validHomeserver = false
if BWIBuildSettings.shared.bwiEnableTokenizedLoginProtection {
let tokenVerificator = ServerTokenVerificator()
let token = await tokenVerificator.fetchToken(baseURL: homeserverAddress)
if let token = token {
validHomeserver = tokenVerificator.verifyToken(baseURL: homeserverAddress, token: token)
}
}
let string = self.normalizeLoginUrl(urlString)
let hashedString = self.hashedString(string)
if BWIBuildSettings.shared.bwiEnableLoginProtection && !validHomeserver {
if let hashes = hashes {
let string = self.normalizeLoginUrl(homeserverAddress)
let hashedString = self.hashedString(string)
validHomeserver = hashes.contains(hashedString)
}
}
return hashes.contains(hashedString)
return validHomeserver
}
private func normalizeLoginUrl(_ urlString: String) -> String {

View File

@@ -16,40 +16,8 @@
*/
import Foundation
/*
let publicKey: Data = publicKeyStr.data(using: .utf8)!
struct MyClaims: Claims {
let version: Int
let hostname: String
}
let signedJWT = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZXJzaW9uIjoxLCJob3N0bmFtZSI6ImJ3aS5kZSJ9.bondtDczLlOHlHLLZj1C9tn60LqBpIhFNaUy6nYL6CVVwWGIv8EIxYWMTx-MP9OSjj-aeMcy0tmDqSz6nbdbgvUJvkB6r-ByH7fTsVj6OtEEs8mWnqHBOFBwTy9tv5vSTfjFX7PBSko2OK3HQrZkFSkfr-xZoOIc_PxblUnec2hClxVq7ImJnIAW1HCh85DMz2c-MiEHd7wQwBcgwWKWmAY9X6uS25WWhQcPH9i0-QMEQNjXGJp-_wM10KJuuOMDx7QdmcX78QgcOyP-G64cA36NL4-6Aby5EnJUDX-uzFbM_ZERgPVmjfzHoZarFCHSK6-fTBg_MQuDF-O2OOdM6Q"
let jwtVerifier = JWTVerifier.rs256(publicKey: publicKey)
do {
let verified = JWT<MyClaims>.verify(signedJWT, using: jwtVerifier)
let newJWT = try JWT<MyClaims>(jwtString: signedJWT, verifier: jwtVerifier)
print(newJWT.claims)
} catch let error {
print(error.localizedDescription)
}
*/
import SwiftJWT
/*
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": 1516239022
*/
struct ServerTokenClaims: Claims {
let issuer: String
let sub: String
@@ -83,12 +51,13 @@ struct ServerTokenVerificator {
if verified && (validated == .success) && matchingHomeServer {
return true
}
} catch let error {
} catch {
// counts like an unverified Token
}
}
return true
// no public key was able to verify the token
return false
}
// fetch the current token from the new endpoint,
@@ -104,7 +73,7 @@ struct ServerTokenVerificator {
config.requestCachePolicy = .reloadIgnoringLocalAndRemoteCacheData
let session : URLSession = URLSession(configuration: config)
let (data, response) = try await session.data(from: url)
let (data, _) = try await session.data(from: url)
// the token may have additional endlines
if let str = String(data: data, encoding: .utf8) {