Frontegg is a web platform where SaaS companies can set up their fully managed, scalable and brand aware - SaaS features and integrate them into their SaaS portals in up to 5 lines of code.
- Get Started
- Project Requirements
- Prepare Frontegg workspace
- Setup Hosted Login
- Add Frontegg package to the project
- Set minimum sdk version
- Configure build config fields
- Initialize FronteggApp
- Enabling Chrome Custom Tabs for Social Login
- Embedded Webview vs Custom Chrome Tab
- Config Android AssetLinks
- Multi-apps Support
- Multi-Region support
- Setup for
Gradle8+
- Usage
- Android SDK 26+ Set defaultConfig's minSDK to 26+ in build.gradle:
Groovy:
android {
defaultConfig {
minSdk 26
}
}
Kotlin:
android {
defaultConfig {
minSdk = 26
}
}
- Java 8+ Set target java 8 byte code for Android and Kotlin plugins respectively build.gradle:
Groovy:
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
Kotlin:
android {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
Navigate to Frontegg Portal Settings, If you don't have application follow integration steps after signing up. Copy FronteggDomain to future steps from Frontegg Portal Domain
- Navigate to Login Method Settings
- Toggle Hosted login method
- Add
{{ANDROID_PACKAGE_NAME}}://{{FRONTEGG_BASE_URL}}/android/oauth/callback
(for custom scheme) - Add
https://{{FRONTEGG_BASE_URL}}/oauth/account/redirect/android/{{ANDROID_PACKAGE_NAME}}
(for assetlinks) - Add
{{FRONTEGG_BASE_URL}}/oauth/authorize
- Replace
ANDROID_PACKAGE_NAME
with your application identifier - Replace
FRONTEGG_BASE_URL
with your Frontegg base url
- Open you project
- Find your app's build.gradle file
- Add the following to your dependencies section:
Groovy:
dependencies {
// Add the Frontegg Android Kotlin SDK
implementation 'com.frontegg.sdk:android:LATEST_VERSION'
// Add Frontegg observables dependency
implementation 'io.reactivex.rxjava3:rxkotlin:3.0.1'
}
Kotlin:
dependencies {
// Add the Frontegg Android Kotlin SDK
implementation ("com.frontegg.sdk:android:LATEST_VERSION")
// Add Frontegg observables dependency
implementation 'io.reactivex.rxjava3:rxkotlin:3.0.1'
}
To set up your Android minimum sdk version, open the root gradle file atandroid/build.gradle
,
and add/edit the minSdkVersion
under buildscript.ext
:
Groovy:
buildscript {
ext {
minSdk = 26
// ...
}
}
Kotlin:
android {
defaultConfig {
minSdk = 26
// ...
}
}
To set up your Android application on to communicate with Frontegg, you have to
add buildConfigField
property the
gradle android/app/build.gradle
.
This property will store frontegg hostname (without https) and client id from previous step:
Groovy:
def fronteggDomain = "FRONTEGG_DOMAIN_HOST.com" // without protocol https://
def fronteggClientId = "FRONTEGG_CLIENT_ID"
android {
defaultConfig {
manifestPlaceholders = [
"package_name" : applicationId,
"frontegg_domain" : fronteggDomain,
"frontegg_client_id": fronteggClientId
]
buildConfigField "String", 'FRONTEGG_DOMAIN', "\"$fronteggDomain\""
buildConfigField "String", 'FRONTEGG_CLIENT_ID', "\"$fronteggClientId\""
}
}
Kotlin:
val fronteggDomain = "FRONTEGG_DOMAIN_HOST.com" // without protocol https://
val fronteggClientId = "FRONTEGG_CLIENT_ID"
android {
defaultConfig {
manifestPlaceholders["package_name"] = applicationId.toString()
manifestPlaceholders["frontegg_domain"] = fronteggDomain
manifestPlaceholders["frontegg_client_id"] = fronteggClientId
buildConfigField("String", "FRONTEGG_DOMAIN", "\"$fronteggDomain\"")
buildConfigField("String", "FRONTEGG_CLIENT_ID", "\"$fronteggClientId\"")
}
}
Add bundleConfig=true if not exists inside the android section inside the app
gradle android/app/build.gradle
Groovy:
android {
buildFeatures {
buildConfig = true
}
}
Kotlin:
android {
buildFeatures {
buildConfig = true
}
}
Create a custom App
class that extends android.app.Application
to initialize FronteggApp
:
package com.frontegg.demo
import android.app.Application
import com.frontegg.android.FronteggApp
class App : Application() {
override fun onCreate() {
super.onCreate()
FronteggApp.init(
BuildConfig.FRONTEGG_DOMAIN,
BuildConfig.FRONTEGG_CLIENT_ID,
this, // Application Context
)
}
}
Register the custom App
in the app's manifest file
AndroidManifest.xml:
<application android:name=".App">
<!-- ... -->
</application>
To enable social login via Chrome Custom Tabs, set the useChromeCustomTabs
flag to true
during the
initialization of FronteggApp
. By default, the SDK uses the Chrome browser for social login.
FronteggApp.init(
BuildConfig.FRONTEGG_DOMAIN,
BuildConfig.FRONTEGG_CLIENT_ID,
this, // Application Context
// ...
useChromeCustomTabs = true
)
Frontegg SDK supports two authentication methods:
- Embedded Webview
- Custom Chrome Tab
By default Frontegg SDK will use Embedded Webview, to use Custom Chrome Tab you have to set remove embedded activity by adding below code to the application manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application>
<!-- ... -->
<activity
android:name="com.frontegg.android.EmbeddedAuthActivity"
android:enabled="false"
tools:replace="android:enabled" />
<activity
android:name="com.frontegg.android.HostedAuthActivity"
android:enabled="true"
tools:replace="android:enabled" />
<!-- ... -->
</application>
</manifest>
Configuring your Android AssetLinks
is required for Magic Link authentication / Reset Password /
Activate Account /
login with IdPs.
To add your AssetLinks
to your Frontegg application, you will need to update in each of your
integrated Frontegg
Environments the AssetLinks
that you would like to use with that Environment. Send a POST request
to https://api.frontegg.com/vendors/resources/associated-domains/v1/android
with the following
payload:
{
"packageName": "YOUR_APPLICATION_PACKAGE_NAME",
"sha256CertFingerprints": ["YOUR_KEYSTORE_CERT_FINGERPRINTS"]
}
Each Android app has multiple certificate fingerprint, to get your DEBUG
sha256CertFingerprint you
have to run the
following command:
For Debug mode, run the following command and copy the SHA-256
value
NOTE: make sure to choose the Variant and Config equals to debug
./gradlew signingReport
###################
# Example Output:
###################
# Variant: debug
# Config: debug
# Store: /Users/davidfrontegg/.android/debug.keystore
# Alias: AndroidDebugKey
# MD5: 25:F5:99:23:FC:12:CA:10:8C:43:F4:02:7D:AD:DC:B6
# SHA1: FC:3C:88:D6:BF:4E:62:2E:F0:24:1D:DB:D7:15:36:D6:3E:14:84:50
# SHA-256: D9:6B:4A:FD:62:45:81:65:98:4D:5C:8C:A0:68:7B:7B:A5:31:BD:2B:9B:48:D9:CF:20:AE:56:FD:90:C1:C5:EE
# Valid until: Tuesday, 18 June 2052
For Release mode, Extract the SHA256 using keytool from your Release
keystore file:
keytool -list -v -keystore /PATH/file.jks -alias YourAlias -storepass *** -keypass ***
In order to use our API’s, follow this guide to generate a vendor token.
This guide outlines the steps to configure your Android application to support multiple applications.
Add FRONTEGG_APPLICATION_ID
buildConfigField into the build.gradle
file:
Groovy:
def fronteggApplicationId = "your-application-id-uuid"
...
android {
...
buildConfigField "String", 'FRONTEGG_APPLICATION_ID', "\"$fronteggApplicationId\""
}
Kotlin:
val fronteggApplicationId = "your-application-id-uuid"
...
android {
...
buildConfigField("String", "FRONTEGG_APPLICATION_ID", "\"$fronteggApplicationId\"")
}
Add BuildConfig
.FRONTEGG_APPLICATION_ID
to FronteggApp
.init
.
Example App.kt code:
class App : Application() {
companion object {
lateinit var instance: App
}
override fun onCreate() {
super.onCreate()
instance = this
FronteggApp.init(
BuildConfig.FRONTEGG_DOMAIN,
BuildConfig.FRONTEGG_CLIENT_ID,
this,
BuildConfig.FRONTEGG_APPLICATION_ID, // here
)
}
}
This guide outlines the steps to configure your Android application to support multiple regions.
First, remove buildConfigFields from your build.gradle
file:
Groovy:
android {
// remove these lines:
// buildConfigField "String", 'FRONTEGG_DOMAIN', "\"$fronteggDomain\""
// buildConfigField "String", 'FRONTEGG_CLIENT_ID', "\"$fronteggClientId\""
}
Kotlin:
android {
// remove these lines:
// buildConfigField("String", "FRONTEGG_DOMAIN", "\"$fronteggDomain\"")
// buildConfigField("String", "FRONTEGG_CLIENT_ID", "\"$fronteggClientId\"")
}
First, adjust your App.kt/java
file to handle multiple regions:
Modifications:
- Remove the existing
FronteggApp.init
function. - Add Call
FronteggApp.initWithRegions
with array ofregions
. This array will hold dictionaries for each region.
Example App.kt code:
class App : Application() {
companion object {
lateinit var instance: App
}
override fun onCreate() {
super.onCreate()
instance = this
FronteggApp.initWithRegions(
listOf(
RegionConfig(
"eu",
"autheu.davidantoon.me",
"b6adfe4c-d695-4c04-b95f-3ec9fd0c6cca"
),
RegionConfig(
"us",
"authus.frontegg.com",
"6903cab0-9809-4a2e-97dd-b8c0f966c813"
)
),
this
)
}
}
For each region, configuring your Android AssetLinks
. This is vital for proper API routing and
authentication.
Follow Config Android AssetLinks to add your Android domains to your
Frontegg application.
The first domain will be placed automatically in the AndroidManifest.xml
file. For each additional
region, you will
need to add an intent-filter
.
Replace ${FRONTEGG_DOMAIN_2}
with the second domain from the previous step.
NOTE: if you are using Custom Chrome Tab
you have to
use android:name
com.frontegg.android.HostedAuthActivity
instead
of com.frontegg.android.EmbeddedAuthActivity
<application>
<activity android:exported="true" android:name="com.frontegg.android.EmbeddedAuthActivity"
tools:node="merge">
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="https"/>
<!-- DONT NOT COMBINE THE FOLLOWING LINES INTO ONE LINE-->
<data android:host="${FRONTEGG_DOMAIN_2}"
android:pathPrefix="/oauth/account/activate"/>
<data android:host="${FRONTEGG_DOMAIN_2}"
android:pathPrefix="/oauth/account/invitation/accept"/>
<data android:host="${FRONTEGG_DOMAIN_2}"
android:pathPrefix="/oauth/account/reset-password"/>
<data android:host="${FRONTEGG_DOMAIN_2}"
android:pathPrefix="/oauth/account/login/magic-link"/>
</intent-filter>
</activity>
<activity android:exported="true" android:name="com.frontegg.android.AuthenticationActivity"
tools:node="merge">
<!-- DONT NOT COMBINE THE FOLLOWING FILTERS INTO ONE LINE-->
<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="${FRONTEGG_DOMAIN_2}"
android:pathPrefix="/oauth/account/redirect/android/${package_name}"
android:scheme="https"/>
</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="${FRONTEGG_DOMAIN_2}" android:scheme="${package_name}"/>
</intent-filter>
</activity>
</application>
The final step is to implement a UI for the user to select their region. This can be done in any way you see fit. The example application uses a simple picker view to allow the user to select their region.
Important Considerations
- Switching Regions: To switch regions, update the selection in Shared Preferences. If issues arise, a re-installation of the application might be necessary.
- Data Isolation: Ensure data handling and APIs are region-specific to prevent data leakage between regions.
Select EU Region | Select US Region |
---|---|
Example Region Selection UI: example code
package com.frontegg.demo
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.LinearLayout
import com.frontegg.android.FronteggApp
class RegionSelectionActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_region_selection)
}
override fun onResume() {
super.onResume()
val euButton = findViewById<LinearLayout>(R.id.euButton)
val usButton = findViewById<LinearLayout>(R.id.usButton)
euButton.setOnClickListener {
FronteggApp.getInstance().initWithRegion("eu")
finish()
}
usButton.setOnClickListener {
FronteggApp.getInstance().initWithRegion("us")
finish()
}
}
}
- Add the below line to your
gradle.properties
:
android.defaults.buildfeatures.buildconfig=true
- Add the below lines to your app/
build.gradle
:
Groovy:
android {
...
buildFeatures {
buildConfig = true
}
...
}
Kotlin:
android {
...
buildFeatures {
buildConfig = true
}
...
}
If minifyEnabled
and shrinkResources
is true follow the instruction below:
# Gson relies on generic type information stored in class files when working with fields.
# ProGuard removes this information by default, so we need to retain it.
-keepattributes Signature
# according to https://stackoverflow.com/a/76224937
# This is also required for R8 in compatibility mode, as several optimizations
# (such as class merging and argument removal) may remove the generic signature.
# For more information, see:
# https://r8.googlesource.com/r8/+/refs/heads/main/compatibility-faq.md#troubleshooting-gson-gson
-keep class com.google.gson.reflect.TypeToken { *; }
-keep class * extends com.google.gson.reflect.TypeToken
# Retain GSON @Expose annotation attributes
-keepattributes AnnotationDefault,RuntimeVisibleAnnotations
-keep class com.google.gson.reflect.TypeToken { <fields>; }
-keepclassmembers class **$TypeAdapterFactory { <fields>; }
# Keep Frontegg classes
-keep class com.frontegg.android.utils.JWT { *; }
-keep class com.frontegg.android.models.** { *; }
# Retain Tink classes used for shared preferences encryption
-keep class com.google.crypto.tink.** { *; }
In order to login with Frontegg, you have to call FronteggAuth.instance.login
method
with activtity
context.
Login method will open Frontegg hosted login page, and will return user data after successful login.
import com.frontegg.android.FronteggAuth
class FirstFragment : Fragment() {
// ...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.loginButton.setOnClickListener {
FronteggAuth.instance.login(requireActivity())
}
}
// ...
}
In order to logout user, you have to call FronteggAuth.instance.logout
method.
Logout method will clear all user data from the device.
import com.frontegg.android.FronteggAuth
class FirstFragment : Fragment() {
// ...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.logoutButton.setOnClickListener {
FronteggAuth.instance.logout()
}
}
// ...
}
In order to switch tenant, you have to call FronteggAuth.instance.switchTenant
method
with activtity
context.
import com.frontegg.android.FronteggAuth
class FirstFragment : Fragment() {
// ...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val tenantIds = FronteggAuth.instance.user.value?.tenantIds ?: listOf()
/**
* pick one from `tenantIds` list:
*/
val tenantToSwitchTo = tenantIds[0]
binding.switchTenant.setOnClickListener {
FronteggAuth.instance.switchTenant(tenantToSwitchTo)
}
}
// ...
}
The Frontegg SDK simplifies integrating passkeys for both registration and login processes
To ensure compatibility with passkey functionality:
-
Update Gradle Dependencies: Add the following dependencies in your
android/build.gradle
:dependencies { implementation 'androidx.browser:browser:1.8.0' implementation 'com.frontegg.sdk:android:1.2.30' }
-
Java Compatibility: Ensure sourceCompatibility and targetCompatibility are set to Java 8 in android/app/build.gradle**:
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
To register a passkey, simply call the registerPasskeys method. This handles the passkey creation process automatically:
FronteggAuth.instance.registerPasskeys(activity!!) {
if (it != null) {
// Handle error
Log.e("FronteggAuth", "Failed to register passkey: $it")
} else {
// Success
Log.i("FronteggAuth", "Passkey registered successfully")
}
}
To log in using an existing passkey, use the loginWithPasskeys method:
FronteggAuth.instance.loginWithPasskeys(activity!!) {
if (it != null) {
// Handle error
Log.e("FronteggAuth", "Failed to log in with passkey: $it")
} else {
// Success
Log.i("FronteggAuth", "Logged in successfully with passkey")
}
}