0

In Android (Android 7.1.1 API 25), I am trying to do an OAuth2 authentication by using Retrofit2 library in the class shown in code snippet 1 below. In onCreate method, I am starting an intent in order to send the authorization request and in onResume method I am trying to capture the authorization code that should be returned by the OAuth server if things go allright. In order to capture the authorization code, I have specified an intent-filter in the android manifest file as shown in the snippet 2 below.

In the onResume method in snippet 1, the uri object that is being returned from getIntent().getData() invocation always return null. Therefore, the execution never enters the following if block and I cannot retrieve the authorization code. Could you please tell me what I am missing here considering what's provided in snippet 1 and 2? Thanks.

SNIPPET 1

package com.xyz.sdk;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;


public class MainActivity extends AppCompatActivity {
private final static String CLIENT_ID = "client";
private final static String CLIENT_SECRET = "secret";

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                    .setAction("Action", null).show();
        }
    });

    StringBuilder authURLBuilder = new StringBuilder();

    authURLBuilder.append("https://id.xyz.com/api/connect/authorize").append("?").
            append("grant_type=").append("password").
            append("&username=").append("86110").
            append("&client_id=").append(CLIENT_ID).
            append("&scope=").append("transfers public accounts offline_access").
            append("&response_type=code&redirect_uri=").
            append("myauth://mycallback");

    /*BEGIN: THIS IS WHERE I AM SENDING AUTHORIZATION REQUEST */
    Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(authURLBuilder.toString()));
    intent.setPackage(this.getClass().getPackage().getName());
    startService(intent);
    /*END: THIS IS WHERE I AM SENDING AUTHORIZATION REQUEST */
}

@Override
protected void onResume(){
    super.onResume();
    Uri uri = getIntent().getData(); //THIS ALWAYS RETURN NULL
    if(uri != null && uri.toString().startsWith(("myauth://mycallback"))) {
        String code = uri.getQueryParameter("code");
        //this.onAuthCodeResponseReceived(code);
    }
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();

    //noinspection SimplifiableIfStatement
    if (id == R.id.action_settings) {
        return true;
    }

    return super.onOptionsItemSelected(item);
}

}

SNIPPET 2

<uses-permission android:name="android.permission.INTERNET" />

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:theme="@style/AppTheme.NoActionBar">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </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:host="mycallback"
                android:scheme="myauth"/>
        </intent-filter>
    </activity>
</application>

P.S.: I have benefited from the retrofit video tutorial in below link and tried to do almost the same as what's explained in the tutorial but thing did not work as explained in the tutorial. Probabaly, I missing some setting but googling did not help much. Video Tutorial Link: https://www.youtube.com/watch?time_continue=726&v=TnQUb-ACqWs

3
  • Still noone has clue? Commented Mar 28, 2018 at 7:55
  • I am sorry but I don't feel that given below answer responds to my original question fully... No matter what happens the uri object that is returned from the getIntent().getData() call is always null. Commented Mar 29, 2018 at 18:57
  • yes it is it worked for me, for last 24 hours i was trying to find out what is problem , why i am getting null and this man has solved Commented Apr 15, 2019 at 6:14

1 Answer 1

1

you are pretty much on track so far but you could make your code handling better by adding a seperate activity, lets call it LoginActivity and set the android:launchMode="singleTop" (I will explain why shortly) as shown below:

<activity android:name=".view.LoginActivity"
        android:theme="@style/AppTheme.NoActionBar"
        android:exported="true"
        android:launchMode="singleTop" >
        <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:host="mycallback"
                android:scheme="myauth" />
        </intent-filter>
    </activity>

Next in your Login Activity make the following changes Hint pay attention to onNewIntent(Intent):

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(this, R.layout.activity_login); 
    // R.layout is the name of your layout, what ever that will be, you may also want to check if the user is logged in or not before running the checkNewIntent carelessly, the logic of how to do this is up to you
    checkNewIntent(getIntent());        
}


@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    checkNewIntent(intent);
}


private void checkNewIntent(Intent intent) {
    if (intent != null && intent.getData() != null) {
        Uri intentUri = intent.getData();
        if(uri != null && uri.toString().startsWith(BuildConfig.REDIRECT_URI)) {
            String code = uri.getQueryParameter("code"); // do something with your code
        } else {
            // show an error toast
        }
    }
}

You maybe wondering why I suggested to use onNewIntent instead of onResume, well I had a similar issue when I started working with oauth callbacks where some browsers besides Chrome would fail call my activity that responds to my callback without delivering any data in the intent, due to how the android system calls the existing activity with the default launch mode.

Using singleTop will assure that the existing activity is not recreated with a new intent but rather brought to front with the new intent, thus onNewIntent() I hope I've explained this well and look forward to your response.

Also added checkNewIntent in the onCreate for special cases such as if your application gets killed in the background for whatever reason, if the user grants permission to your application the onNewIntent may not be called if the activity is destroyed, well at least from my experience, I'm sure someone might have a better explanation for this :)

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks for your answer. I am new to android development and I wonder how can I associate this login activity with my normal Mainactivity. Are they tied on the fly by the runtime?
You could just have a simple button labelled as login, where you'll just use an intent to start that activity with an intent, after the login is a success you would close your login activity making your main activity go onto the onResume() at which you'll put in checks to see if the user is logged in: see android life cycles. Let me know if the answer I provided worked for you
Could you provide the entire LoginActivity class source code please? When I created a Login activity using Android Studio, it generates code quite different from what you have provided.
getPresenter().isAuthenticated() What do we need in order to get this bit of code compiled? I would appreicate the full class code for the LoginActivity.
The other boiler plate code I added won't be generated, I only added that to give you a hint of how you would check if the user is logged in or not, I have edited the code, Note you will have to override the onNewIntent method
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.