Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 08c0f768 authored by Fahim Salam Chowdhury's avatar Fahim Salam Chowdhury 👽
Browse files

Merge branch '6584-Refactor_openId_signIn_flow' into 'main'

6584-Refactor_openId_signIn_flow

See merge request !101
parents 2a4cf902 b465f2cd
Loading
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -6,6 +6,9 @@ stages:

before_script:
  - echo email.key=$PEPPER >> local.properties
  - echo GOOGLE_CLIENT_ID=$GOOGLE_CLIENT_ID >> local.properties
  - echo GOOGLE_REDIRECT_URI=$GOOGLE_REDIRECT_URI >> local.properties
  - echo YAHOO_CLIENT_ID=$YAHOO_CLIENT_ID >> local.properties
  - export GRADLE_USER_HOME=$(pwd)/.gradle
  - chmod +x ./gradlew

+11 −5
Original line number Diff line number Diff line
@@ -86,7 +86,7 @@ android {

            shrinkResources true

            buildConfigField "String", "EMAIL_KEY", "\"${retrieveEmailKey()}\""
            buildConfigField "String", "EMAIL_KEY", "\"${retrieveKey("email.key")}\""
        }
    }

@@ -101,8 +101,14 @@ android {
    }

    defaultConfig {
        buildConfigField "String", "GOOGLE_CLIENT_ID", "\"${retrieveKey("GOOGLE_CLIENT_ID")}\""
        buildConfigField "String", "GOOGLE_REDIRECT_URI", "\"${retrieveKey("GOOGLE_REDIRECT_URI")}\""

        buildConfigField "String", "YAHOO_CLIENT_ID", "\"${retrieveKey("YAHOO_CLIENT_ID")}\""

	    manifestPlaceholders = [
                'appAuthRedirectScheme': 'net.openid.appauthdemo'
                'appAuthRedirectScheme':  applicationId,
                "googleAuthRedirectScheme": retrieveKey("GOOGLE_REDIRECT_URI")
        ]
    }
}
@@ -195,13 +201,13 @@ dependencies {
    testImplementation "com.squareup.okhttp3:mockwebserver:${versions.okhttp}"
}

def retrieveEmailKey() {
def retrieveKey(String keyName) {
    Properties properties = new Properties()
    properties.load(project.rootProject.file('local.properties').newDataInputStream())

    String value = properties.getProperty("email.key")
    String value = properties.getProperty(keyName)
    if (value == null) {
        throw new GradleException("email.key property not found in local.properties file")
        throw new GradleException(keyName + " property not found in local.properties file")
    }

    return value
+2 −2
Original line number Diff line number Diff line
@@ -567,8 +567,8 @@
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />

                <data android:scheme="net.openid.appauthdemo" />
                <data android:scheme="com.googleusercontent.apps.100496780587-pbiu5eudcjm6cge2phduc6mt8mgbsmsr" />
                <data android:scheme="${appAuthRedirectScheme}" />
                <data android:scheme="${googleAuthRedirectScheme}" />
            </intent-filter>

        </activity>
+32 −0
Original line number Diff line number Diff line
/*
 * Copyright MURENA SAS 2023
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package at.bitfire.davdroid

import net.openid.appauth.ClientAuthentication
import net.openid.appauth.ClientSecretBasic
import net.openid.appauth.NoClientAuthentication

object OpenIdUtils {

    fun getClientAuthentication(secret: String?): ClientAuthentication {
        if (secret == null) {
            return NoClientAuthentication.INSTANCE
        }

        return ClientSecretBasic(secret)
    }
}
+58 −158
Original line number Diff line number Diff line
/*
 * Copyright ECORP SAS 2022
 * Copyright MURENA SAS 2022, 2023
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
@@ -16,200 +16,108 @@

package at.bitfire.davdroid.authorization;

import android.content.Context;
import android.content.res.Resources;
import android.net.Uri;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;

import net.openid.appauth.AuthorizationServiceConfiguration;
import net.openid.appauth.AuthorizationServiceConfiguration.RetrieveConfigurationCallback;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

import at.bitfire.davdroid.R;
import at.bitfire.davdroid.BuildConfig;

/**
 * An abstraction of identity providers, containing all necessary info for the demo app.
 */
public class IdentityProvider {

    /**
     * Value used to indicate that a configured property is not specified or required.
     */
    public static final int NOT_SPECIFIED = -1;

    public static final IdentityProvider GOOGLE = new IdentityProvider(
            "Google",
            R.string.google_discovery_uri,
            NOT_SPECIFIED, // auth endpoint is discovered
            NOT_SPECIFIED, // token endpoint is discovered
            R.string.google_client_id,
            NOT_SPECIFIED, // client secret is not required for Google
            R.string.google_auth_redirect_uri,
            R.string.google_scope_string,
            R.string.google_name);

    public static final List<IdentityProvider> PROVIDERS = Arrays.asList(
            GOOGLE);

    public static List<IdentityProvider> getEnabledProviders(Context context) {
        ArrayList<IdentityProvider> providers = new ArrayList<>();
        for (IdentityProvider provider : PROVIDERS) {
            provider.readConfiguration(context);
            providers.add(provider);
        }
        return providers;
    }

    @NonNull
    public final String name;

    @StringRes
    public final int buttonContentDescriptionRes;

    @StringRes
    private final int mDiscoveryEndpointRes;
            "https://accounts.google.com/.well-known/openid-configuration",
            null,
            null,
            BuildConfig.GOOGLE_CLIENT_ID,
            null,
            BuildConfig.GOOGLE_REDIRECT_URI + ":/oauth2redirect",
            "openid profile email https://www.googleapis.com/auth/carddav https://www.googleapis.com/auth/calendar https://mail.google.com/",
            null
    );

    @StringRes
    private final int mAuthEndpointRes;

    @StringRes
    private final int mTokenEndpointRes;
    @Nullable
    private final Uri mDiscoveryEndpoint;

    @StringRes
    private final int mClientIdRes;
    @Nullable
    private final Uri mAuthEndpoint;

    @StringRes
    private final int mClientSecretRes;
    @Nullable
    private final Uri mTokenEndpoint;
    @NonNull
    private final String mClientId;

    @StringRes
    private final int mRedirectUriRes;
    @Nullable
    private final String mClientSecret;
    @NonNull
    private final Uri mRedirectUri;

    @StringRes
    private final int mScopeRes;
    @Nullable
    private final String mScope;

    private boolean mConfigurationRead = false;
    private Uri mDiscoveryEndpoint;
    private Uri mAuthEndpoint;
    private Uri mTokenEndpoint;
    private String mClientId;
    private String mClientSecret;
    private Uri mRedirectUri;
    private String mScope;
    @Nullable
    private final String mUserInfoEndpoint;

    IdentityProvider(
            @NonNull String name,
            @StringRes int discoveryEndpointRes,
            @StringRes int authEndpointRes,
            @StringRes int tokenEndpointRes,
            @StringRes int clientIdRes,
            @StringRes int clientSecretRes,
            @StringRes int redirectUriRes,
            @StringRes int scopeRes,
            @StringRes int buttonContentDescriptionRes) {
        if (!isSpecified(discoveryEndpointRes)
                && !isSpecified(authEndpointRes)
                && !isSpecified(tokenEndpointRes)) {
            @Nullable String discoveryEndpoint,
            @Nullable String authEndpoint,
            @Nullable String tokenEndpoint,
            @NonNull String clientId,
            @Nullable String clientSecret,
            @NonNull String redirectUri,
            @Nullable String scope,
            @Nullable String userInfoEndpoint) {
        if (discoveryEndpoint == null &&
                (authEndpoint == null || tokenEndpoint == null)) {
            throw new IllegalArgumentException(
                    "the discovery endpoint or the auth and token endpoints must be specified");
        }

        this.name = name;
        this.mDiscoveryEndpointRes = discoveryEndpointRes;
        this.mAuthEndpointRes = authEndpointRes;
        this.mTokenEndpointRes = tokenEndpointRes;
        this.mClientIdRes = checkSpecified(clientIdRes, "clientIdRes");
        this.mClientSecretRes = clientSecretRes;
        this.mRedirectUriRes = checkSpecified(redirectUriRes, "redirectUriRes");
        this.mScopeRes = checkSpecified(scopeRes, "scopeRes");
        this.buttonContentDescriptionRes =
                checkSpecified(buttonContentDescriptionRes, "buttonContentDescriptionRes");
    }

    /**
     * This must be called before any of the getters will function.
     */
    public void readConfiguration(Context context) {
        if (mConfigurationRead) {
            return;
        }

        Resources res = context.getResources();

        mDiscoveryEndpoint = isSpecified(mDiscoveryEndpointRes)
                ? getUriResource(res, mDiscoveryEndpointRes, "discoveryEndpointRes")
                : null;
        mAuthEndpoint = isSpecified(mAuthEndpointRes)
                ? getUriResource(res, mAuthEndpointRes, "authEndpointRes")
                : null;
        mTokenEndpoint = isSpecified(mTokenEndpointRes)
                ? getUriResource(res, mTokenEndpointRes, "tokenEndpointRes")
                : null;
        mClientId = res.getString(mClientIdRes);
        mClientSecret = isSpecified(mClientSecretRes) ? res.getString(mClientSecretRes) : null;
        mRedirectUri = getUriResource(res, mRedirectUriRes, "mRedirectUriRes");
        mScope = res.getString(mScopeRes);

        mConfigurationRead = true;
    }

    private void checkConfigurationRead() {
        if (!mConfigurationRead) {
            throw new IllegalStateException("Configuration not read");
        }
    }

    @Nullable
    public Uri getDiscoveryEndpoint() {
        checkConfigurationRead();
        return mDiscoveryEndpoint;
    }

    @Nullable
    public Uri getAuthEndpoint() {
        checkConfigurationRead();
        return mAuthEndpoint;
    }

    @Nullable
    public Uri getTokenEndpoint() {
        checkConfigurationRead();
        return mTokenEndpoint;
        this.mDiscoveryEndpoint = retrieveUri(discoveryEndpoint);
        this.mAuthEndpoint = retrieveUri(authEndpoint);
        this.mTokenEndpoint = retrieveUri(tokenEndpoint);
        this.mClientId = clientId;
        this.mClientSecret = clientSecret;
        this.mRedirectUri = Objects.requireNonNull(retrieveUri(redirectUri));
        this.mScope = scope;
        this.mUserInfoEndpoint = userInfoEndpoint;
    }

    @NonNull
    public String getClientId() {
        checkConfigurationRead();
        return mClientId;
    }

    @Nullable
    public String getClientSecret() {
        checkConfigurationRead();
        return mClientSecret;
    }

    @NonNull
    public Uri getRedirectUri() {
        checkConfigurationRead();
        return mRedirectUri;
    }

    @NonNull
    public String getScope() {
        checkConfigurationRead();
        return mScope;
    }

    public void retrieveConfig(Context context,
                               RetrieveConfigurationCallback callback) {
        readConfiguration(context);
        if (getDiscoveryEndpoint() != null) {
    @Nullable
    public String getUserInfoEndpoint() {
        return mUserInfoEndpoint;
    }

    public void retrieveConfig(RetrieveConfigurationCallback callback) {
        if (mDiscoveryEndpoint != null) {
            AuthorizationServiceConfiguration.fetchFromUrl(mDiscoveryEndpoint, callback);
        } else {
            AuthorizationServiceConfiguration config =
@@ -218,19 +126,11 @@ public class IdentityProvider {
        }
    }

    private static boolean isSpecified(int value) {
        return value != NOT_SPECIFIED;
    }

    private static int checkSpecified(int value, String valueName) {
        if (value == NOT_SPECIFIED) {
            throw new IllegalArgumentException(valueName + " must be specified");
    @Nullable
    private Uri retrieveUri(@Nullable String value) {
        if (value == null) {
            return null;
        }
        return value;
        return Uri.parse(value);
    }

    private static Uri getUriResource(Resources res, @StringRes int resId, String resName) {
        return Uri.parse(res.getString(resId));
}
}
Loading