0

I am trying to complete an OAuth2 authentication in a Delphi fmx Android app. I am using Delphi 11.0. When I created the app's credentails in the Google console I selected Android as the app type. As a result there is no client secret and I use the reverse DNS of my client ID, with a path of '/oauth2redirect', as the redirection endpoint in my requests to Google's OAuth2 server. My question is how to capture the OAuth2 server's response. There are many SO posts related to this but none that I could find addresses this question.

I have created an intent-filter in my Android manifest:

<intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <data android:scheme="com.googleusercontent.apps.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" />
</intent-filter>

There are other intent filters of type VIEW to get the app to respond to certain file types:

    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:mimeType="application/pdf" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data
            android:scheme="content"
            android:mimeType="application/vnd.xxxxxxxxxx"
            android:pathPattern=".*\\.xxx" />
        <data
            android:scheme="content"
            android:mimeType="application/xxxxxxxxxx"
            android:pathPattern=".*\\.xxx" />
        <data
            android:scheme="content"
            android:mimeType="application/octet-stream"
            android:pathPattern=".*\\.xxx" />
    </intent-filter>

In the app I try to capture the intent:

function TViewMasterDetail.HandleIntentAction(const aIntent: JIntent): Boolean;
var
    lURI: JNet_Uri;
//    lMimeType: string;
    lNameSize: TNameSizeRecord;
    lSourceFilePath: String;
    lDestFilePath: string;
    lInput: JInputStream;
    lOutput: TFileStream;
    lData: Integer;
    lBuffer: TBytes;
    i: Integer;
begin
    Result := False;
    if aIntent <> nil then
    begin
TUtils.Log('Action: ' + JStringToString(aIntent.getAction)); //always empty string
        if TJIntent.JavaClass.ACTION_VIEW.equals(aIntent.getAction) then
        begin
//            lMimeType := JStringToString(aIntent.getType);
            lURI := aIntent.getData;
            if assigned(lURI) then
            begin
TUtils.Log('Scheme: ' + JStringToString(lURI.getScheme));
                if JStringToString(lURI.getScheme) = RedirectionEndpoint then
                begin
TUtils.Log('Path: ' + JStringToString(lURI.getPath)); //never get here
                end
                else
                begin
                    lSourceFilePath := JStringToString(lURI.getPath);
                    lNameSize := ExtractNameSize(lURI);
                    lDestFilePath := TPath.Combine(TPath.GetTempPath, lNameSize.name);

//https://jenkov.com/tutorials/java-io/inputstream.html
                    SetLength(lBuffer, lNameSize.size);
                    lInput := SharedActivity.getContentResolver.openInputStream(lURI);
                    i := 0;
                    lData := lInput.read;
                    while lData <> -1 do
                    begin
                        lBuffer[i] := lData;
                        i := i + 1;
                        lData := lInput.read;
                    end;
                    lOutput := TFileStream.Create(lDestFilePath, fmCreate);
                    lOutput.WriteBuffer(lBuffer, lNameSize.size);
                    Viewmodel.HandleExternalFile(lDestFilePath);
//                    TFile.Delete(lDestFilePath);

                    lOutput.Free;
                end;
            end;
        end;
    end;
end;

This code is called when I switch back to the app after completing Google's login screen. Breakpoints tell me that there is an intent in the call, but its action type is not VIEW. Trying to print the action type to the console always results in an empty string.

Is this the approach for capturing the authentication server's response in the case of an Android app using a custom URI scheme? What do I need to do?

Thanks

EDIT

This is how HandleIntentAction is called:

procedure TViewMasterDetail.FormCreate(Sender: TObject);
var
    lAppEventService: IFMXApplicationEventService;
...
//C:\Users\Public\Documents\Embarcadero\Studio\21.0\Samples\Object Pascal\Mobile Snippets\AndroidIntents
    if TPlatformServices.Current.SupportsPlatformService(IFMXApplicationEventService,
        lAppEventService) then
    lAppEventService.SetApplicationEventHandler(HandleAppEvent);

    // Register the type of intent action that we want to be able to receive.
    // Note: A corresponding <action> tag must also exist in the <intent-filter> section of AndroidManifest.template.xml.
    MainActivity.registerIntentAction(TJIntent.JavaClass.ACTION_VIEW);
    TMessageManager.DefaultManager.SubscribeToMessage(TMessageReceivedNotification, HandleActivityMessage);
...
end;

procedure TViewMasterDetail.HandleActivityMessage(const Sender: TObject; const
    aMessage: TMessage);
begin
    if aMessage is TMessageReceivedNotification then
        HandleIntentAction(TMessageReceivedNotification(aMessage).Value);
end;

function TViewMasterDetail.HandleAppEvent(aAppEvent: TApplicationEvent;
    aContext: TObject): Boolean;
var
    lStartupIntent: JIntent;
begin
  Result := False;
    if aAppEvent = TApplicationEvent.BecameActive then
    begin
        lStartupIntent := MainActivity.getIntent;
        if lStartupIntent <> nil then
            HandleIntentAction(lStartupIntent);
    end;
end;

EDIT 2

In the last comment to the answer to this SO question there is information that should be relevant to my question. But I can't figure out how to turn that comment into code. Unfortunately, people at Google often shy away from Delphi questions, otherwise I would try and get a fuller explanation from the poster. Perhaps the meaning of the comment is clear to someone???

David U
  • 943
  • 1
  • 8
  • 22
  • How/when is `HandleIntentAction` being called? Your code and description does not make this clear – Dave Nottage Apr 09 '23 at 07:21
  • @DaveNottage I've edited my post to answer your question. Thanks. – David U Apr 10 '23 at 08:20
  • If your app is already running, when your app becomes active after the Google login, it will likely call HandleIntentAction twice, since the `BecameActive` event will fire, and if the app is being started because of the login, `HandleActivityMessage` should also be called. Can you verify that the latter is being called at all? – Dave Nottage Apr 10 '23 at 20:52
  • A quick run tells me HandleActivityMessage is never called. I get only 1 call to HandleIntentAction after login. I had already begun to suspect that the one call is 'AppReturnedToForeground' (my words) and I am barking up the wrong tree. Also, something I did not mention is after Google authentication I see a 'Open In Another App?' dialog in the browser, which makes no sense to me. So I must have seriously misunderstood something... – David U Apr 11 '23 at 07:12
  • I suspect the issue is in your intent filter(s), but I am yet to know enough about this process to be of any help. I've stuck it on my "todo" list – Dave Nottage Apr 11 '23 at 20:58

0 Answers0