Skip to content

Commit 2e41249

Browse files
committed
Support for stable OAuth 2.0 API discovery with fallback to unstable MSC2965 .well-known
Uses the /_matrix/client/v1/auth_metadata endpoint if available. Uses discovery to choose the MSC4191 action parameter.
1 parent b0f967c commit 2e41249

File tree

12 files changed

+327
-0
lines changed

12 files changed

+327
-0
lines changed

MatrixSDK/Background/MXBackgroundStore.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,10 @@ class MXBackgroundStore: NSObject, MXStore {
253253
func storeHomeserverCapabilities(_ homeserverCapabilities: MXCapabilities) {
254254
}
255255

256+
var authMetadata: MXAuthMetadata?
257+
func store(_ authMetadata: MXAuthMetadata) {
258+
}
259+
256260
var supportedMatrixVersions: MXMatrixVersions?
257261
func storeSupportedMatrixVersions(_ supportedMatrixVersions: MXMatrixVersions) {
258262
}

MatrixSDK/Data/Store/MXFileStore/MXFileStore.m

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,20 @@ - (void)storeHomeserverCapabilities:(MXCapabilities *)capabilities
493493
}
494494
}
495495

496+
- (MXAuthMetadata *)authMetadata
497+
{
498+
return metaData.authMetadata;
499+
}
500+
501+
- (void)storeAuthMetadata:(MXAuthMetadata *)authMetadata
502+
{
503+
if (metaData)
504+
{
505+
metaData.authMetadata = authMetadata;
506+
metaDataHasChanged = YES;
507+
}
508+
}
509+
496510
- (MXMatrixVersions *)supportedMatrixVersions
497511
{
498512
return metaData.supportedMatrixVersions;

MatrixSDK/Data/Store/MXFileStore/MXFileStoreMetaData.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#import "MXWellKnown.h"
2020
#import "MXCapabilities.h"
2121
#import "MXMatrixVersions.h"
22+
#import "MXAuthMetadata.h"
2223

2324
@interface MXFileStoreMetaData : NSObject <NSCoding, NSCopying>
2425

@@ -78,4 +79,9 @@
7879
*/
7980
@property (nonatomic) NSInteger maxUploadSize;
8081

82+
/**
83+
OAuth 2.0 server metadata.
84+
*/
85+
@property (nonatomic) MXAuthMetadata *authMetadata;
86+
8187
@end

MatrixSDK/Data/Store/MXMemoryStore/MXMemoryStore.m

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ @implementation MXMemoryStore
4141
@synthesize storeService, eventStreamToken, userAccountData, syncFilterId, homeserverWellknown, areAllIdentityServerTermsAgreed;
4242
@synthesize homeserverCapabilities;
4343
@synthesize supportedMatrixVersions;
44+
@synthesize authMetadata;
4445

4546
- (instancetype)init
4647
{
@@ -410,6 +411,11 @@ - (void)storeSupportedMatrixVersions:(MXMatrixVersions *)supportedMatrixVersions
410411
supportedMatrixVersions = supportedMatrixVersions;
411412
}
412413

414+
- (void)storeAuthMetadata:(MXAuthMetadata *)authMetadata
415+
{
416+
authMetadata = authMetadata;
417+
}
418+
413419
- (NSInteger)maxUploadSize
414420
{
415421
return self->maxUploadSize;

MatrixSDK/Data/Store/MXNoStore/MXNoStore.m

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,14 @@ - (void)storeHomeserverCapabilities:(MXCapabilities *)homeserverCapabilities
480480
{
481481
}
482482

483+
- (MXAuthMetadata *)authMetadata
484+
{
485+
return nil;
486+
}
487+
- (void)storeAuthMetadata:(MXAuthMetadata *)authMetadata
488+
{
489+
}
490+
483491
- (MXMatrixVersions *)supportedMatrixVersions
484492
{
485493
return nil;

MatrixSDK/Data/Store/MXStore.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
@class MXStoreService;
3535
@class MXCapabilities;
3636
@class MXMatrixVersions;
37+
@class MXAuthMetadata;
3738

3839
/**
3940
The `MXStore` protocol defines an interface that must be implemented in order to store
@@ -407,6 +408,18 @@
407408
*/
408409
- (void)storeSupportedMatrixVersions:(nonnull MXMatrixVersions*)supportedMatrixVersions;
409410

411+
/**
412+
The homeserver OAuth 2.0 server metadata.
413+
*/
414+
@property (nonatomic, readonly) MXAuthMetadata * _Nullable authMetadata;
415+
416+
/**
417+
Store the homeserver OAuth 2.0 server metadata.
418+
419+
@param authMetadata the homeserver OAuth 2.0 server metadata to store.
420+
*/
421+
- (void)storeAuthMetadata:(nonnull MXAuthMetadata*)authMetadata;
422+
410423
#pragma mark - Room Messages
411424

412425
/**
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//
2+
// Copyright 2025 The Matrix.org Foundation C.I.C
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
//
16+
17+
#import <Foundation/Foundation.h>
18+
19+
#import "MXJSONModel.h"
20+
21+
NS_ASSUME_NONNULL_BEGIN
22+
23+
/**
24+
Subset of OAuth 2.0 API Server metadata: https://spec.matrix.org/v1.16/client-server-api/#server-metadata-discovery
25+
{
26+
"issuer": "https://example.com/",
27+
"account_management_uri": "https://example.com/account",
28+
"account_management_actions_supported": ["org.matrix.profile", "org.matrix.device_delete"]
29+
}
30+
*/
31+
32+
@interface MXAuthMetadata : MXJSONModel<NSCoding>
33+
34+
@property (nonatomic, readonly) NSString *issuer;
35+
@property (nonatomic, readonly, nullable) NSString *accountManagementUri;
36+
@property (nonatomic, readonly, nullable) NSArray<NSString*> *accountManagementActionsSupported;
37+
38+
-(NSURL * _Nullable) getLogoutDeviceURLFromID: (NSString * ) deviceID;
39+
40+
@end
41+
42+
NS_ASSUME_NONNULL_END
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
//
2+
// Copyright 2025 The Matrix.org Foundation C.I.C
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
//
16+
17+
#import "MXAuthMetadata.h"
18+
19+
static NSString *const kMXIssuer = @"issuer";
20+
static NSString *const kMXAccountManagementUri = @"account_management_uri";
21+
static NSString *const kMXAccountManagementActionsSupported = @"account_management_actions_supported";
22+
23+
@interface MXAuthMetadata ()
24+
25+
@property (nonatomic, readwrite) NSString *issuer;
26+
@property (nonatomic, readwrite, nullable) NSString *accountManagementUri;
27+
@property (nonatomic, readwrite, nullable) NSArray<NSString*> *accountManagementActionsSupported;
28+
29+
@end
30+
31+
@implementation MXAuthMetadata
32+
33+
+ (instancetype)modelFromJSON:(NSDictionary *)JSONDictionary
34+
{
35+
MXAuthMetadata *oauthMetadata;
36+
37+
NSString *issuer;
38+
MXJSONModelSetString(issuer, JSONDictionary[kMXIssuer]);
39+
40+
if (issuer)
41+
{
42+
oauthMetadata = [[MXAuthMetadata alloc] init];
43+
oauthMetadata.issuer = issuer;
44+
MXJSONModelSetString(oauthMetadata.accountManagementUri, JSONDictionary[kMXAccountManagementUri])
45+
MXJSONModelSetArray(oauthMetadata.accountManagementActionsSupported, JSONDictionary[kMXAccountManagementActionsSupported])
46+
}
47+
48+
return oauthMetadata;
49+
}
50+
51+
-(NSURL * _Nullable) getLogoutDeviceURLFromID: (NSString * ) deviceID
52+
{
53+
if (!_accountManagementUri)
54+
{
55+
return nil;
56+
}
57+
NSURLComponents *components = [NSURLComponents componentsWithString:_accountManagementUri];
58+
// default to the stable value
59+
NSString *actionParam = @"org.matrix.device_delete";
60+
if ([_accountManagementActionsSupported containsObject: @"org.matrix.delete_device"]) {
61+
// use the stable value
62+
} else if ([_accountManagementActionsSupported containsObject: @"org.matrix.session_end"]) {
63+
// earlier version of MSC4191
64+
actionParam = @"org.matrix.session_end";
65+
} else if ([_accountManagementActionsSupported containsObject: @"session_end"]) {
66+
// previous unspecced version
67+
actionParam = @"session_end";
68+
}
69+
70+
components.queryItems = @[
71+
[NSURLQueryItem queryItemWithName:@"device_id" value:deviceID],
72+
[NSURLQueryItem queryItemWithName:@"action" value:actionParam]
73+
];
74+
return components.URL;
75+
}
76+
77+
78+
#pragma mark - NSCoding
79+
80+
- (id)initWithCoder:(NSCoder *)aDecoder
81+
{
82+
self = [super init];
83+
if (self)
84+
{
85+
_issuer = [aDecoder decodeObjectForKey:kMXIssuer];
86+
_accountManagementUri = [aDecoder decodeObjectForKey: kMXAccountManagementUri];
87+
_accountManagementActionsSupported = [aDecoder decodeObjectForKey: kMXAccountManagementActionsSupported];
88+
}
89+
return self;
90+
}
91+
92+
- (void)encodeWithCoder:(NSCoder *)aCoder
93+
{
94+
[aCoder encodeObject:_issuer forKey:kMXIssuer];
95+
[aCoder encodeObject:_accountManagementUri forKey:kMXAccountManagementUri];
96+
[aCoder encodeObject:_accountManagementActionsSupported forKey:kMXAccountManagementActionsSupported];
97+
}
98+
99+
@end

MatrixSDK/MXRestClient.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#import "MXTaggedEvents.h"
4646
#import "MXCredentials.h"
4747
#import "MXRoomAliasResolution.h"
48+
#import "MXAuthMetadata.h"
4849

4950
@class MXThirdpartyProtocolsResponse;
5051
@class MXThirdPartyUsersResponse;
@@ -3107,6 +3108,20 @@ Note: Clients should consider avoiding this endpoint for URLs posted in encrypte
31073108
- (MXHTTPOperation*)homeServerCapabilitiesWithSuccess:(void (^)(MXHomeserverCapabilities *capabilities))success
31083109
failure:(void (^)(NSError *error))failure NS_REFINED_FOR_SWIFT;
31093110

3111+
#pragma mark - Homeserver OAuth 2.0 server metadata
3112+
3113+
/**
3114+
Get the homeserver OAuth 2.0 server metadata.
3115+
3116+
@param success A block object called when the operation succeeds. It provides
3117+
the metadata.
3118+
@param failure A block object called when the operation fails.
3119+
3120+
@return a MXHTTPOperation instance.
3121+
*/
3122+
- (MXHTTPOperation*)authMetadata:(void (^)(MXAuthMetadata *authMetadata))success
3123+
failure:(void (^)(NSError *error))failure;
3124+
31103125
@end
31113126

31123127
MX_ASSUME_MISSING_NULLABILITY_END

MatrixSDK/MXRestClient.m

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,34 @@ - (MXHTTPOperation *)capabilities:(void (^)(MXCapabilities *))success
601601
return operation;
602602
}
603603

604+
- (MXHTTPOperation*)authMetadata:(void (^)(MXAuthMetadata *authMetadata))success
605+
failure:(void (^)(NSError *error))failure
606+
{
607+
NSString *path = @"_matrix/client/v1/auth_metadata";
608+
609+
MXWeakify(self);
610+
return [httpClient requestWithMethod:@"GET"
611+
path:path
612+
parameters:nil
613+
success:^(NSDictionary *JSONResponse) {
614+
MXStrongifyAndReturnIfNil(self);
615+
616+
if (success)
617+
{
618+
__block MXAuthMetadata *authMetadata;
619+
[self dispatchProcessing:^{
620+
MXJSONModelSetMXJSONModel(authMetadata, MXAuthMetadata, JSONResponse);
621+
} andCompletion:^{
622+
success(authMetadata);
623+
}];
624+
}
625+
}
626+
failure:^(NSError *error) {
627+
MXStrongifyAndReturnIfNil(self);
628+
[self dispatchFailure:error inBlock:failure];
629+
}];
630+
}
631+
604632
#pragma mark - Registration operations
605633
- (MXHTTPOperation *)testUserRegistration:(NSString *)username callback:(void (^)(MXError *mxError))callback
606634
{

0 commit comments

Comments
 (0)