openid / openid/AppAuth-iOS

application:openURL:options: not called

Open
#356 11 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

question
Dominant language
Objective-C
Stars
2k
Forks
867
Avg merge
4d 48m
Merged PRs (30d)
1

Description

I'm using this in Flutter where I have just the rootViewController. It all works well I can login, the server redirects to the custom scheme of the app.

In my case I redirect the code to the my backend server and from there to the app. After the server responds the app pops up but the callback is not called, nether the application:openURL:options:

What am I missing.

#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"

static NSString *const kAppAuthExampleAuthStateKey = @"authState";

@implementation AppDelegate {
    OIDServiceConfiguration *configuration;
    OIDAuthState *authState;
    FlutterResult flutterResult;
}

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [GeneratedPluginRegistrant registerWithRegistry:self];
  
    FlutterViewController *controller = (FlutterViewController *) [[self window] rootViewController];
    FlutterMethodChannel *channel =
        [FlutterMethodChannel methodChannelWithName:@"app_channel" binaryMessenger: controller];

    [channel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
        [self onMethodCall:call result:result];
    }];
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

    
- (void) onMethodCall:(FlutterMethodCall *)call result:(FlutterResult )result {
    if([@"init" isEqualToString:call.method]){
      OIDServiceDiscovery *doc = [[OIDServiceDiscovery alloc] initWithJSON:call.arguments error:nil];
      configuration = [[OIDServiceConfiguration alloc] initWithDiscoveryDocument:doc];
    } else if([@"authorize" isEqualToString:call.method]) {
        if (flutterResult) {
            flutterResult = nil;
            @throw [NSInvalidArgumentException initWithString:@"Already login in."];
        }
        flutterResult = result;
        NSString *jsonString = call.arguments[@"params"];
        NSStringEncoding encoding = 0;
        NSData *jsonData = [jsonString dataUsingEncoding:encoding];
        NSError *error=nil;
        NSDictionary *params = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&error];
        
        NSMutableDictionary *additionalParameters = [[NSMutableDictionary alloc] initWithDictionary: params[@"additionalParameters"]];
        
        if (params[@"display"]) {
            [additionalParameters setValue:params[@"display"] forKey:@"display"];
        }
        
        if (params[@"prompt"]) {
            [additionalParameters setValue:params[@"prompt"] forKey:@"prompt"];
        }
        NSString *nonce = additionalParameters[@"nonce"];
        [additionalParameters removeObjectForKey: @"nonce"];
        
        OIDAuthorizationRequest *request =
        [[OIDAuthorizationRequest alloc]
         initWithConfiguration: configuration
         clientId: params[@"clientId"]
         clientSecret:nil
         scope: params[@"scope"]
         redirectURL: [NSURL URLWithString:params[@"redirectUri"]]
         responseType: params[@"responseType"]
         state: params[@"state"]
         nonce: nonce
         codeVerifier: nil
         codeChallenge: nil
         codeChallengeMethod: nil
         additionalParameters: additionalParameters];
        
        _currentAuthorizationFlow = [OIDAuthState
         authStateByPresentingAuthorizationRequest:request
         presentingViewController:[[self window] rootViewController]
         callback:^(OIDAuthState * _Nullable authState, NSError * _Nullable error) {
             NSLog(@"%@", [[authState lastAuthorizationResponse] additionalParameters]);
             if (authState) {
                 [self setAuthState:authState];
                 
             } else {
                 result([FlutterError errorWithCode:error.description message:error.description details:nil]);
                 [self setAuthState:nil];
             }
         }];
    } else {
        result(FlutterMethodNotImplemented);
    }
}
    
- (BOOL)application:(UIApplication *)app
            openURL:(NSURL *)url
            options:(NSDictionary<NSString *, id> *)options {
    
    NSLog(@"a| %@", [url absoluteString]);
    
    // Sends the URL to the current authorization flow (if any) which will
    // process it if it relates to an authorization response.
    if ([_currentAuthorizationFlow resumeExternalUserAgentFlowWithURL:url]) {
        _currentAuthorizationFlow = nil;
        return YES;
    }
    
    NSLog(@"%@", [url absoluteString]);
    
    // Your additional URL handling (if any) goes here.
    
    return NO;
}
    
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
    NSLog(@"%@", [url absoluteString]);
    return [self application:application openURL:url options:@{}];
}
    
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
    NSLog(@"%@", [url absoluteString]);
    return [self application:application openURL:url options:@{}];
}
    
- (void)didChangeState:(OIDAuthState *)state {
    [self saveState];
}
    
- (void)saveState {
    // for production usage consider using the OS Keychain instead
    NSData *archivedAuthState = [ NSKeyedArchiver archivedDataWithRootObject:_authState];
    [[NSUserDefaults standardUserDefaults] setObject:archivedAuthState
                                              forKey:kAppAuthExampleAuthStateKey];
    [[NSUserDefaults standardUserDefaults] synchronize];
}
    
- (void)loadState {
    // loads OIDAuthState from NSUSerDefaults
    NSData *archivedAuthState =
    [[NSUserDefaults standardUserDefaults] objectForKey:kAppAuthExampleAuthStateKey];
    OIDAuthState *authState = [NSKeyedUnarchiver unarchiveObjectWithData:archivedAuthState];
    [self setAuthState:authState];
}
    
- (void)setAuthState:(nullable OIDAuthState *)authState {
    if (_authState == authState) {
        return;
    }
    _authState = authState;
    _authState.stateChangeDelegate = self;
    [self saveState];
}
    
@end

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the AppDelegate URL handlers and the _currentAuthorizationFlow setup shown in the issue, then trace whether the custom-scheme callback reaches application:openURL:options:. Compare the callback lifecycle with the AppAuth-iOS integration guidance; done means the authorization flow resumes and the Flutter result receives the outcome.

Written by the indexing model from the issue text.

Assessment

Tech stack
flutter, objective-c
Domain
authentication, mobile-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.