[ios] Switched OSM Oauth to using a link

Signed-off-by: Yannik Bloscheck <git@yannikbloscheck.com>
This commit is contained in:
Yannik Bloscheck
2025-06-24 16:39:25 +02:00
committed by Konstantin Pastbin
parent 126d4f6373
commit 090b7c21fc
11 changed files with 98 additions and 25 deletions

View File

@@ -10,6 +10,7 @@
#import <CoreApi/CoreApi.h>
#import "Bridging.h"
#import "DeepLinkRouteStrategyAdapter.h"
#import "EAGLView.h"
#import "FirstSession.h"

7
iphone/Maps/Bridging.h Normal file
View File

@@ -0,0 +1,7 @@
@interface Bridging: NSObject
+ (void)saveOauthTokenFrom:(NSString *)oauthCode;
@end

23
iphone/Maps/Bridging.mm Normal file
View File

@@ -0,0 +1,23 @@
#import "Bridging.h"
#include "base/logging.hpp"
#include "editor/server_api.hpp"
#include "platform/platform.hpp"
#include "private.h"
#import "MWMAuthorizationCommon.h"
using namespace osm;
@implementation Bridging
+ (void)saveOauthTokenFrom:(NSString * _Nonnull)oauthCode
{
NSString *oauthToken = @(OsmOAuth::ServerAuth().FinishAuthorization([oauthCode UTF8String]).c_str());
OsmOAuth::ServerAuth().SetAuthToken([oauthToken UTF8String]);
osm_auth_ios::AuthorizationStoreCredentials([oauthToken UTF8String]);
}
@end

View File

@@ -73,6 +73,13 @@ static inline CGFloat LengthCGPoint(CGPoint point)
/// @param externally: If true, try to open URL in installed application or in Safari, otherwise open in internal browser without leaving the app.
- (BOOL)openUrl:(NSString *)urlString externally:(BOOL)externally;
/// Open URL externally in installed application (or in Safari if there are no appropriate application) if possible or internally in SFSafariViewController. Returns NO (false) if the url id invalid.
///
/// @param urlString: URL string to open.
/// @param externally: If true, try to open URL in installed application or in Safari, otherwise open in internal browser without leaving the app.
/// @param skipEncoding: If true, extra URL encoding will be skipped
- (BOOL)openUrl:(NSString *)urlString externally:(BOOL)externally skipEncoding:(BOOL)skipEncoding;
@end
@interface UIImage (ImageWithColor)

View File

@@ -175,6 +175,11 @@
}
- (BOOL)openUrl:(NSString *)urlString externally:(BOOL)externally
{
return [self openUrl:urlString externally:externally skipEncoding:NO];
}
- (BOOL)openUrl:(NSString *)urlString externally:(BOOL)externally skipEncoding:(BOOL)skipEncoding
{
// TODO: This is a temporary workaround to open cyrillic/non-ASCII URLs.
// URLs in OSM are stored in UTF-8. NSURL constructor documentation says:
@@ -183,13 +188,19 @@
// 1. Split the (non-ASCII) string into components (host, path, query, fragment, etc.)
// 2. Encode each component separately (they have different allowed characters).
// 3. Merge them back into the string and create NSURL.
NSMutableCharacterSet * charset = [[NSMutableCharacterSet alloc] init];
[charset formUnionWithCharacterSet:NSCharacterSet.URLHostAllowedCharacterSet];
[charset formUnionWithCharacterSet:NSCharacterSet.URLPathAllowedCharacterSet];
[charset formUnionWithCharacterSet:NSCharacterSet.URLQueryAllowedCharacterSet];
[charset formUnionWithCharacterSet:NSCharacterSet.URLFragmentAllowedCharacterSet];
[charset addCharactersInString:@"#;/?:@&=+$,"];
NSString * encoded = [urlString stringByAddingPercentEncodingWithAllowedCharacters:charset];
NSString * encoded;
if (skipEncoding) {
encoded = urlString;
} else {
NSMutableCharacterSet * charset = [[NSMutableCharacterSet alloc] init];
[charset formUnionWithCharacterSet:NSCharacterSet.URLHostAllowedCharacterSet];
[charset formUnionWithCharacterSet:NSCharacterSet.URLPathAllowedCharacterSet];
[charset formUnionWithCharacterSet:NSCharacterSet.URLQueryAllowedCharacterSet];
[charset formUnionWithCharacterSet:NSCharacterSet.URLFragmentAllowedCharacterSet];
[charset addCharactersInString:@"#;/?:@&=+$,"];
encoded = [urlString stringByAddingPercentEncodingWithAllowedCharacters:charset];
}
// Matrix has an url with two hashes which doesn't work for NSURL and NSURLComponent.
NSRange const matrixUrl = [encoded rangeOfString:@"#/#"];
if (matrixUrl.location != NSNotFound)

View File

@@ -36,15 +36,14 @@ extern NSString * const kMap2GoogleLoginSegue;
- (IBAction)osmTap
{
[self close:^{
[self.alertController.ownerViewController performSegueWithIdentifier:kMap2OsmLoginSegue
sender:nil];
[self.alertController.ownerViewController openUrl:@(osm::OsmOAuth::ServerAuth().BuildOAuth2Url().c_str()) externally:NO skipEncoding:YES];
}];
}
- (IBAction)signUpTap
{
[self close:^{
[self.alertController.ownerViewController openUrl:@(osm::OsmOAuth::ServerAuth().GetRegistrationURL().c_str())];
[self.alertController.ownerViewController openUrl:@(osm::OsmOAuth::ServerAuth().GetRegistrationURL().c_str()) externally:YES];
}];
}

View File

@@ -1,7 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15705" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23727" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15706"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@@ -9,11 +11,11 @@
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="J1a-qv-gDF" customClass="MWMOsmAuthAlert" propertyAccessControl="none">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="TM8-yS-nAN" userLabel="ContainerView">
<rect key="frame" x="47.5" y="137" width="280" height="393"/>
<rect key="frame" x="56.666666666666657" y="229.66666666666663" width="280" height="393"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="zn5-ic-jDO" userLabel="Done" customClass="MWMButton">
<rect key="frame" x="240" y="0.0" width="40" height="40"/>
@@ -35,7 +37,7 @@
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="iX9-ov-Ks7" userLabel="Title">
<rect key="frame" x="20" y="140" width="240" height="61"/>
<rect key="frame" x="20" y="140.00000000000003" width="240" height="61"/>
<constraints>
<constraint firstAttribute="width" constant="240" id="6y2-gu-OZJ"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="30" id="uAq-DT-bJU"/>
@@ -50,8 +52,8 @@
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="login_to_make_edits_visible"/>
</userDefinedRuntimeAttributes>
</label>
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="1Oa-Ks-27P" userLabel="Google">
<rect key="frame" x="20" y="221" width="115" height="44"/>
<button hidden="YES" opaque="NO" userInteractionEnabled="NO" alpha="0.0" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="1Oa-Ks-27P" userLabel="Google">
<rect key="frame" x="20" y="221.00000000000003" width="115" height="43.999999999999972"/>
<constraints>
<constraint firstAttribute="width" constant="115" id="Wxi-Ef-iFz"/>
<constraint firstAttribute="height" constant="44" id="jZt-Vg-20q"/>
@@ -62,8 +64,8 @@
<action selector="googleTap" destination="J1a-qv-gDF" eventType="touchUpInside" id="W4r-Gx-ylI"/>
</connections>
</button>
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="o9z-GX-feY" userLabel="Facebook">
<rect key="frame" x="145" y="221" width="115" height="44"/>
<button hidden="YES" opaque="NO" userInteractionEnabled="NO" alpha="0.0" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="o9z-GX-feY" userLabel="Facebook">
<rect key="frame" x="145" y="221.00000000000003" width="115" height="43.999999999999972"/>
<constraints>
<constraint firstAttribute="width" constant="115" id="OxE-Ml-Dmd"/>
<constraint firstAttribute="height" constant="44" id="fmf-GO-Qsi"/>
@@ -150,12 +152,12 @@
</variation>
</view>
</subviews>
<viewLayoutGuide key="safeArea" id="Z2W-ia-IOB"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="TM8-yS-nAN" firstAttribute="centerX" secondItem="J1a-qv-gDF" secondAttribute="centerX" id="rY8-Ie-aSn"/>
<constraint firstItem="TM8-yS-nAN" firstAttribute="centerY" secondItem="J1a-qv-gDF" secondAttribute="centerY" id="t5z-q7-9wM"/>
</constraints>
<viewLayoutGuide key="safeArea" id="Z2W-ia-IOB"/>
<point key="canvasLocation" x="305" y="172"/>
</view>
</objects>

View File

@@ -49,7 +49,7 @@ static NSString * const kMap2OsmLoginSegue = @"Map2OsmLogin";
- (IBAction)osmTap
{
[self close:^{
[self.alertController.ownerViewController performSegueWithIdentifier:kMap2OsmLoginSegue sender:nil];
[self.alertController.ownerViewController openUrl:@(osm::OsmOAuth::ServerAuth().BuildOAuth2Url().c_str()) externally:NO skipEncoding:YES];
}];
}

View File

@@ -86,7 +86,8 @@ using namespace osm_auth_ios;
{
[self performOnlineAction:^
{
[self performSegueWithIdentifier:kOSMAuthSegue sender:self.loginOSMButton];
[self openUrl:@(OsmOAuth::ServerAuth().BuildOAuth2Url().c_str()) externally:NO skipEncoding:YES];
}];
}
@@ -94,7 +95,7 @@ using namespace osm_auth_ios;
{
[self performOnlineAction:^
{
[self openUrl:@(OsmOAuth::ServerAuth().GetRegistrationURL().c_str())];
[self openUrl:@(OsmOAuth::ServerAuth().GetRegistrationURL().c_str()) externally:YES];
}];
}

View File

@@ -130,9 +130,25 @@
// Not supported on iOS.
return false;
case .oAuth2:
// TODO: support OAuth2
return false;
var components = url.absoluteString.components(separatedBy: "cm://oauth2/osm/callback?code=")
components.removeAll { component in
component.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
}
if let code = components.first {
Bridging.saveOauthToken(from: code)
let window = (UIApplication.shared.connectedScenes.filter { $0.activationState == .foregroundActive }.first(where: { $0 is UIWindowScene }) as? UIWindowScene)?.keyWindow
window?.rootViewController?.presentedViewController?.navigationController?.popToRootViewController(animated: true)
window?.rootViewController?.presentedViewController?.dismiss(animated: true)
return true
} else {
return false
}
case .incorrect:
if url.absoluteString.starts(with: "cm://oauth2/osm/callback") {
let window = (UIApplication.shared.connectedScenes.filter { $0.activationState == .foregroundActive }.first(where: { $0 is UIWindowScene }) as? UIWindowScene)?.keyWindow
window?.rootViewController?.presentedViewController?.navigationController?.popToRootViewController(animated: true)
window?.rootViewController?.presentedViewController?.dismiss(animated: true)
}
// Invalid URL or API parameters.
return false;
@unknown default:

View File

@@ -10,6 +10,7 @@
039371B62C5B68CD00708377 /* UIFont+monospaced.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039371B52C5B68CD00708377 /* UIFont+monospaced.swift */; };
165953742CB1D85500CFED7C /* fonts in Resources */ = {isa = PBXBuildFile; fileRef = 165953732CB1D83700CFED7C /* fonts */; };
1DFA2F6A20D3B57400FB2C66 /* UIColor+PartnerColor.m in Sources */ = {isa = PBXBuildFile; fileRef = 1DFA2F6920D3B57400FB2C66 /* UIColor+PartnerColor.m */; };
275830E92E0ACABA00EF4883 /* Bridging.mm in Sources */ = {isa = PBXBuildFile; fileRef = 275830E82E0ACAB800EF4883 /* Bridging.mm */; };
3304306D21D4EAFB00317CA3 /* SearchCategoryCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3304306C21D4EAFB00317CA3 /* SearchCategoryCell.swift */; };
33046832219C57180041F3A8 /* CategorySettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33046831219C57180041F3A8 /* CategorySettingsViewController.swift */; };
337F98A621D37B7400C8AC27 /* SearchTabViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 337F98A521D37B7400C8AC27 /* SearchTabViewController.swift */; };
@@ -780,6 +781,8 @@
1DFA2F6820D3B52F00FB2C66 /* UIColor+PartnerColor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIColor+PartnerColor.h"; sourceTree = "<group>"; };
1DFA2F6920D3B57400FB2C66 /* UIColor+PartnerColor.m */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; lineEnding = 0; path = "UIColor+PartnerColor.m"; sourceTree = "<group>"; };
1DFA2F6E20D3CA9200FB2C66 /* UIColorRoutines.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UIColorRoutines.h; sourceTree = "<group>"; };
275830E72E0ACAAB00EF4883 /* Bridging.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Bridging.h; sourceTree = "<group>"; };
275830E82E0ACAB800EF4883 /* Bridging.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = Bridging.mm; sourceTree = "<group>"; };
28A0AB4B0D9B1048005BE974 /* Maps_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = Maps_Prefix.pch; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
29B97316FDCFA39411CA2CEA /* main.mm */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = main.mm; sourceTree = "<group>"; };
30034C5C2B3F0B74005D961A /* az */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = az; path = az.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -1954,6 +1957,8 @@
29B97314FDCFA39411CA2CEA /* Maps */ = {
isa = PBXGroup;
children = (
275830E72E0ACAAB00EF4883 /* Bridging.h */,
275830E82E0ACAB800EF4883 /* Bridging.mm */,
FA456C4026BDCC8E00B83C20 /* shaders.xcodeproj */,
FA36B8011540388B004560CC /* Bookmarks */,
3454D7981E07F045004AF2AD /* Categories */,
@@ -4533,6 +4538,7 @@
3404F48B202894EA0090E401 /* BMCViewController.swift in Sources */,
F6E2FDE01E097BA00083EBEC /* MWMEditorViewController.mm in Sources */,
6741A9C01BF340DE002C974C /* MWMTextView.m in Sources */,
275830E92E0ACABA00EF4883 /* Bridging.mm in Sources */,
F6E2FDB61E097BA00083EBEC /* MWMEditorAdditionalNamesHeader.m in Sources */,
F6E2FDC81E097BA00083EBEC /* MWMEditorNotesFooter.m in Sources */,
F6E2FD651E097BA00083EBEC /* MWMMapDownloaderPlaceTableViewCell.m in Sources */,