Skip to content

Commit

Permalink
Feature - login prompt (#87)
Browse files Browse the repository at this point in the history
* feat: login prompt content provider implementation

* refactor: content provider interaction logic moved to SessionInfoManager class

* feat: fetching content provider authorities from the package manager; checking is session on the device exists

* chore: refactored loginPrompt classes code style

* refactor: move sessionInfoManager to sharedPreferencesStorage class

* refactor: fix compatibility issues with PackageManager.MATCH_ALL

* refactor: login prompt content provider refactor on xserxses comments

* Add loginPromptManager (#73)

* Add loginPromptManager

* Add translations (#82)

* call loginPrompt from Client (#84)

* call loginPrompt from Client

clean main activity
Run requestLoginPrompt in background thread

* Login promp tracking (#80)

* Propose SchibstedAccountTracking public and internal API

* Present tracking API in ExampleApp

* Document API

* More readable logging

* Initial events for show/hide login prompt

* Tracking events for clicks

* Update events (#86)

* Add final tracking events

---------

Co-authored-by: filip-misztal <[email protected]>
Co-authored-by: bogdan-niculescu-sch <[email protected]>

* Fill in readme (#89)

- apply outstanding review remark

* Throw different error type if user cancels login (#83)

* Throw different error type if user cancels login

* Check error before state

- throw NotAuthed.CancelledByUser error

* Fix typo

* Update webflows/src/main/java/com/schibsted/account/webflows/util/Util.kt

Co-authored-by: Filip Misztal <[email protected]>

* Review remarks before merge (#90)

* initial cleanup

* make tracking thread safe

- small review remarks

* cleanup layout

* code cleanup

* add localized logos

* Update logos

* fix dialog showing check

* apply review remark

* Fix query period on content provider getSessions

* Change DB primary key to packageName for content provider (#91)

* Change db primary key to packagename for content provider

* On conclict - replace with new values

* user writable database for writting

---------

Co-authored-by: filip-misztal <[email protected]>

* Use "use" to be more safe in case of failures + Nice syntax (#92)

* Use use to be more safe

* Even more idiomatic Kotlin

---------

Co-authored-by: filip-misztal <[email protected]>

* Send cancel event on eid user cancel (#93)

* Send cancel event on eid user cancel

* Small Readme update

* Login prompt crash (#94)

* Pass intent via argument instead of whole client

* Prevent adding twice

---------

Co-authored-by: filip-misztal <[email protected]>

* add support for norsk bokmal and norsk nynorsk (#96)

* add serverUrl to content provider query (#95)

* check for local session before showing login prompt (#97)

* check for local session before showing login prompt

* apply review remark

* Check also for presence - not only callback type (#98)

Co-authored-by: filip-misztal <[email protected]>

* Dismiss prompt when login is initiated (#99)

Co-authored-by: filip-misztal <[email protected]>

* Remove login promp on login click (#100)

* Dismiss prompt when login is initiated

* Better place

* This is no longer needed

---------

Co-authored-by: filip-misztal <[email protected]>

* add extra properties for events (#101)

* add extra properties for events

* Update readme and minor cleanup

---------

Co-authored-by: wbaklazec-sch <[email protected]>
Co-authored-by: bogdan-niculescu-sch <[email protected]>
Co-authored-by: filip-misztal <[email protected]>
  • Loading branch information
4 people authored Oct 6, 2023
1 parent 695f214 commit 0fd7338
Show file tree
Hide file tree
Showing 38 changed files with 1,096 additions and 28 deletions.
47 changes: 44 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ New implementation of the Schibsted account Android SDK using the web flows via
[Custom Tabs](https://developer.chrome.com/docs/android/custom-tabs/overview/):

* API documentation can be
found [here](https://pages.github.schibsted.io/spt-identity/account-sdk-android-web/).
found [here](https://pages.github.schibsted.io/spt-identity/account-sdk-android-web/).
* An example implementation of the SDK can be found
[here](https://github.schibsted.io/spt-identity/account-sdk-android-web/tree/master/app/src/main/java/com/schibsted/account/example).
[here](https://github.schibsted.io/spt-identity/account-sdk-android-web/tree/master/app/src/main/java/com/schibsted/account/example).

## Getting started

Expand All @@ -33,6 +33,7 @@ will help you create a client and configure the necessary data.

The SDK is available via
[Maven Central](https://search.maven.org/artifact/com.schibsted.account/account-sdk-android-web):

```
implementation 'com.schibsted.account:account-sdk-android-web:<version>'
```
Expand Down Expand Up @@ -217,11 +218,51 @@ by following these steps:
.create(<Your service interface>)
}
```

The SDK will automatically inject the user access token as a Bearer token in the HTTP
Authorization request header. If the access token is rejected with a `401 Unauthorized`
response (e.g. due to having expired), the SDK will try to use the refresh token to obtain a
new access token and then retry the request once more.

**Note:** If the refresh token request fails, due to the refresh token itself having expired
or been invalidated by the user, the SDK will log the user out.

#### Login prompt

This is a light version of simplified-login, allowing mobile app developers integrating this
SDK to prompt users for log in if a valid session was already detected on the device.
This feature is making use of the single-sign on feature from web, allowing users to log in with
only two taps.

**Note** that for this feature to work, both the app where the user has a valid session, and the app
that implements and
requests the login prompt need to use Android SDK Web version 6.1.0 or newer.

**Note** that it is the calling app's responsibility to request the login prompt only if the user is
not
already logged in, or if any other specific conditions are met.

Example:

```kotlin
if (!user?.isLoggedIn()) {
lifecycleScope.launch {
ExampleApp.client.requestLoginPrompt(applicationContext, supportFragmentManager, true)
}
}
```

#### Pulse tracking

We have also added a way of integrating the app's Pulse instance into the SDK allowing internal
events to be sent.

**Important:** When integrating with the SDK defined events, please keep in mind that the Pulse
instance needs to map `[SchibstedAccountTrackingEvent].providerComponent` to `provider.component`
and `[SchibstedAccountTrackingEvent].deployTag` to `deploy_tag` before pushing the created events.

This is a temporary solution and will be subject to changes in the future, but in the meanwhile
you can use the provided example from ExampleApp to connect your Pulse event transmitter, which
will be then used internally to track login-prompt flows and also send events if the login was
successful or not.

4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'

android {
compileSdkVersion 32
compileSdkVersion 34

defaultConfig {
applicationId "com.schibsted.account"
minSdkVersion 21
targetSdkVersion 30
targetSdkVersion 32
versionCode 1
versionName "1.0"

Expand Down
14 changes: 14 additions & 0 deletions app/src/main/java/com/schibsted/account/example/ExampleApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import com.schibsted.account.example.MainActivity.Companion.LOGIN_FAILED_EXTRA
import com.schibsted.account.webflows.activities.AuthorizationManagementActivity
import com.schibsted.account.webflows.client.Client
import com.schibsted.account.webflows.client.ClientConfiguration
import com.schibsted.account.webflows.tracking.SchibstedAccountTrackerStore
import com.schibsted.account.webflows.tracking.SchibstedAccountTrackingEvent
import com.schibsted.account.webflows.tracking.SchibstedAccountTrackingListener
import timber.log.Timber

class ExampleApp : Application() {
Expand All @@ -22,6 +25,17 @@ class ExampleApp : Application() {
initManualClient()
initAuthorizationManagement()
initTimber()
initTracking()
}

private fun initTracking() {
val listener = object : SchibstedAccountTrackingListener {
override fun onEvent(event: SchibstedAccountTrackingEvent) {
Timber.d("Tracked event ${event::class.simpleName.toString()}")
}
}

SchibstedAccountTrackerStore.addTrackingListener(listener)
}

private fun initTimber() {
Expand Down
19 changes: 17 additions & 2 deletions app/src/main/java/com/schibsted/account/example/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import com.schibsted.account.databinding.ActivityMainBinding
import com.schibsted.account.webflows.activities.AuthResultLiveData
import com.schibsted.account.webflows.activities.NotAuthed
import com.schibsted.account.webflows.user.User
import com.schibsted.account.webflows.util.Either
import kotlinx.coroutines.launch
import timber.log.Timber

class MainActivity : AppCompatActivity() {
Expand All @@ -19,7 +21,6 @@ class MainActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)

binding = ActivityMainBinding.inflate(layoutInflater)

setContentView(binding.root)

if (intent.getBooleanExtra(LOGIN_FAILED_EXTRA, false)) {
Expand All @@ -31,6 +32,14 @@ class MainActivity : AppCompatActivity() {
observeAuthResultLiveData()
}

override fun onResume() {
super.onResume()

lifecycleScope.launch {
ExampleApp.client.requestLoginPrompt(applicationContext, supportFragmentManager, true)
}
}


private fun initializeButtons() {
binding.loginButton.setOnClickListener {
Expand Down Expand Up @@ -73,7 +82,13 @@ class MainActivity : AppCompatActivity() {
}

private fun startLoggedInActivity(user: User) {
startActivity(LoggedInActivity.intentWithUser(this, user, LoggedInActivity.Companion.Flow.AUTOMATIC))
startActivity(
LoggedInActivity.intentWithUser(
this,
user,
LoggedInActivity.Companion.Flow.AUTOMATIC
)
)
}

companion object {
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@id/loginButton" />

</androidx.constraintlayout.widget.ConstraintLayout>
4 changes: 2 additions & 2 deletions app/src/main/res/values/colors.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#6200EE</color>
<color name="colorPrimaryDark">#3700B3</color>
<color name="colorPrimary">#3274D4</color>
<color name="colorPrimaryDark">#2196F3</color>
<color name="colorAccent">#03DAC5</color>
</resources>
7 changes: 6 additions & 1 deletion app/src/main/res/values/styles.xml
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="toolbarStyle">@style/AppToolbar</item>
</style>

<style name="AppToolbar" parent="Widget.MaterialComponents.Toolbar.Primary">
<item name="titleTextColor">@android:color/white</item>
</style>

</resources>
16 changes: 13 additions & 3 deletions webflows/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,26 @@ apply plugin: 'maven-publish'
apply plugin: 'signing'

android {
compileSdkVersion 32
compileSdkVersion 34

defaultConfig {
minSdkVersion 21
targetSdkVersion 30
targetSdkVersion 33

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
resConfigs "en", "sv", "no", "nn", "nb", "fi", "dk"
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}

buildFeatures {
viewBinding true
}

buildTypes {
release {
minifyEnabled false
Expand Down Expand Up @@ -60,15 +65,20 @@ dependencies {

api "androidx.browser:browser:1.4.0"
implementation 'com.nimbusds:nimbus-jose-jwt:9.24.3'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'

//Logging
implementation "com.jakewharton.timber:timber:$timber_version"

//Login prompt
implementation 'androidx.constraintlayout:constraintlayout:2.2.0-alpha12'
implementation 'com.google.android.material:material:1.9.0'

testImplementation 'junit:junit:4.13.2'
testImplementation "io.mockk:mockk:${mockkVersion}"
testImplementation "com.squareup.okhttp3:mockwebserver:${okHttpVersion}"

testImplementation 'org.robolectric:robolectric:4.8.2'
testImplementation 'org.robolectric:robolectric:4.9.1'
testImplementation 'androidx.test.ext:junit:1.1.3'
testImplementation "androidx.test.espresso:espresso-core:${espressoVersion}"
testImplementation "androidx.test.espresso:espresso-intents:${espressoVersion}"
Expand Down
14 changes: 13 additions & 1 deletion webflows/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@
<uses-permission android:name="android.permission.INTERNET" />

<application>
<provider
android:name="com.schibsted.account.webflows.loginPrompt.LoginPromptContentProvider"
android:authorities="${applicationId}.contentprovider"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.schibsted.account.LOGIN_PROMPT_CONTENT_PROVIDER" />
</intent-filter>
</provider>
<activity
android:name="com.schibsted.account.webflows.activities.AuthorizationManagementActivity"
android:exported="false"
Expand All @@ -15,5 +24,8 @@
<intent>
<action android:name="android.support.customtabs.action.CustomTabsService" />
</intent>
<intent>
<action android:name="com.schibsted.account.LOGIN_PROMPT_CONTENT_PROVIDER" />
</intent>
</queries>
</manifest>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import androidx.annotation.MainThread
import androidx.lifecycle.LiveData
import com.schibsted.account.webflows.client.Client
import com.schibsted.account.webflows.client.LoginError
import com.schibsted.account.webflows.tracking.SchibstedAccountTracker
import com.schibsted.account.webflows.tracking.SchibstedAccountTrackingEvent
import com.schibsted.account.webflows.user.User
import com.schibsted.account.webflows.util.Either
import com.schibsted.account.webflows.util.Either.Left
Expand Down Expand Up @@ -61,7 +63,10 @@ class AuthResultLiveData private constructor(private val client: Client) :
is Left -> update(
Left(
when (result.value) {
is LoginError.CancelledByUser -> NotAuthed.CancelledByUser
is LoginError.CancelledByUser -> {
SchibstedAccountTracker.track(SchibstedAccountTrackingEvent.UserLoginCanceled)
NotAuthed.CancelledByUser
}
else -> NotAuthed.LoginFailed(result.value)
}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import android.content.Intent
import android.net.Uri
import android.os.Bundle
import com.schibsted.account.webflows.client.Client
import com.schibsted.account.webflows.tracking.SchibstedAccountTracker
import com.schibsted.account.webflows.tracking.SchibstedAccountTrackingEvent
import com.schibsted.account.webflows.util.Either.Left
import timber.log.Timber

Expand Down Expand Up @@ -177,6 +179,7 @@ class AuthorizationManagementActivity : Activity() {

private fun handleAuthorizationCanceled() {
Timber.d("Authorization flow canceled by user")
SchibstedAccountTracker.track(SchibstedAccountTrackingEvent.UserLoginCanceled)
AuthResultLiveData.get().update(Left(NotAuthed.CancelledByUser))
cancelIntent?.send()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ data class Address(
@SerializedName("invoice")
INVOICE;

override fun toString(): String = super.toString().toLowerCase(Locale.ROOT)
override fun toString(): String = super.toString().lowercase(Locale.ROOT)
}
}

Expand Down
Loading

0 comments on commit 0fd7338

Please sign in to comment.