Flutter & Java Quickstart

This is a quickstart guide to help you integrate Descope with your Flutter & Java application. Follow the steps below to get started.

Include the Descope Flutter Package

Incorporate the Descope Flutter SDK as a dependency. Run the following command:

Terminal
flutter pub add descope

(iOS Only) Set minimum global platform version to 13.0

  1. Navigate to ios/Podfile
  2. Uncomment platform :ios, '11.0'
  3. Set the version to 13.0
Podfile
# Uncomment this line to define a global platform for your project
platform :ios, '13.0' # <-- Change this to 13.0
 
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
 
# ...

(Android Only) Set minimum global platform version to 24

  1. Navigate to android/app/build.gradle
  2. Replace minSdkVersion flutter.minSdkVersion with minSdkVersion 24
build.gradle
// ...
android {
    // ...
    defaultConfig {
        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
        applicationId "com.example.flutter_sample_app"
        // You can update the following values to match your application needs.
        // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
        minSdkVersion 24 // <-- Change this to 24
        targetSdkVersion flutter.targetSdkVersion
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
    }
    // ...
}
// ...

Import the Flutter SDK

Proceed to import the Descope Flutter dependency.

main.dart
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:descope/descope.dart'; // <-- Import the Descope SDK
 
Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
 
  runApp(const MyApp());
}
 
class MyApp extends StatelessWidget {
  const MyApp({super.key});
 
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Descope Flutter Sample App',
        theme: ThemeData(
          colorScheme: ColorScheme.fromSeed(seedColor: Colors.green),
          useMaterial3: true,
        ),
        home: Descope.sessionManager.session?.refreshToken.isExpired == false
            ? const HomeScreen()
            : const WelcomeScreen());
  }
}
 
class WelcomeScreen extends StatefulWidget {
  const WelcomeScreen({super.key});
 
  @override
  State<WelcomeScreen> createState() => _WelcomeScreenState();
}
 
// ...

Initialize Descope with Custom Project ID

Set up Descope in main.dart using the Descope Project ID found on your Descope project page. This is needed to activate the SDK.

Note

Optionally, add the baseUrl parameter if using a CNAME in your Descope project (e.g., https://auth.company.com).

main.dart
Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
 
  Descope.setup('__ProjectID__');
 
  await Descope.sessionManager.loadSession();
 
  runApp(const MyApp());
}

Define and Host Your Flow

Your Descope console provides customizable, predefined authentication flows which are hosted for you. You can also host the flow yourself. You'll use the url of the hosted flow later in your code. If you are deploying to Web, you can set the flow id and container css in the options here as well. For more on web flows set up, read about it in our SDK README.

main.dart
class WelcomeScreen extends StatefulWidget {
  const WelcomeScreen({super.key});
 
  @override
  State<WelcomeScreen> createState() => _WelcomeScreenState();
}
 
class _WelcomeScreenState extends State<WelcomeScreen> {
  Future<void> _startFlow() async {
    try {
      final options = DescopeFlowOptions(
        mobile: DescopeMobileFlowOptions(
            flowUrl: 'https://auth.descope.io/login/__ProjectID__', // <-- Your flow URL
			deepLinkUrl: '<your_deep_link_url>' // <-- Your deep link URL
		),
        web: DescopeWebFlowOptions(
          flowId: 'sign-up-or-in',
          flowContainerCss: {
            "background-color": "antiquewhite",
            "width": "500px",
            "min-height": "300px",
            "margin": "auto",
            "position": "relative",
            "top": "50%",
            "transform": "translateY(-50%)",
            "display": "flex",
            "flex-direction": "column",
            "align-items": "center",
            "justify-content": "center",
            "box-shadow": "0px 0px 10px gray",
          },
        ));
    } catch (e) {
      print('Error: $e');
      return;
    }
  }

For Android (iOS works with just a flow url), follow the instructions in our SDK README to establish App Links, create an Activity, and manage routing.

Launch your Flow

Use the flow 'start' function to initiate authentication with a custom flow (URL from step 6). For Android, add the deep link (URL from step 7).

Note

If you're using Magic Link authentication, additional setup is required as mentioned in our README.

main.dart
class _WelcomeScreenState extends State<WelcomeScreen> {
  Future<void> _startFlow() async {
    try {
      final options = DescopeFlowOptions(
        mobile: DescopeMobileFlowOptions(
            flowUrl: 'https://auth.descope.io/login/__ProjectID__',
			deepLinkUrl: '<your_deep_link_url>'
		),
        web: DescopeWebFlowOptions(
          flowId: 'sign-up-or-in',
          flowContainerCss: {
            "background-color": "antiquewhite",
            "width": "500px",
            "min-height": "300px",
            "margin": "auto",
            "position": "relative",
            "top": "50%",
            "transform": "translateY(-50%)",
            "display": "flex",
            "flex-direction": "column",
            "align-items": "center",
            "justify-content": "center",
            "box-shadow": "0px 0px 10px gray",
          },
        ));
 
      final authResponse = await Descope.flow.start(options); // <-- Start the flow
      final session = DescopeSession.fromAuthenticationResponse(authResponse);
      Descope.sessionManager.manageSession(session);
 
      if (!mounted) return;
      Navigator.of(context).push(
        MaterialPageRoute(
          builder: (context) => const HomeScreen(),
        ),
      );
    } catch (e) {
      print('Error: $e');
      return;
    }
  }
}

Leverage Flutter SDK's Session Management Functions

Descope offers the Session Manager to confirm user authentication or access user details like email, userId, etc. Personalize the user experience with:

  • user - Access user attributes (email, name, etc.)
  • logout - Revoke and clear active session
  • refreshToken?.isExpired - Check if the user is authenticated

For more specifics on implementing Session Management, check out our Mobile Session Validation page.

For backend Session Validation, continue reading!

SceneDelegate.swift
class _HomeScreenState extends State<HomeScreen> {
  DescopeUser? user = Descope.sessionManager.session?.user;
 
  Future<void> _logout() async {
    final refreshJwt = Descope.sessionManager.session?.refreshJwt;
    if (refreshJwt != null) {
      await Descope.auth.logout(refreshJwt);
      Descope.sessionManager.clearSession();
    }
 
    if (!mounted) return;
    Navigator.of(context).push(MaterialPageRoute(
      builder: (context) => const WelcomeScreen(),
    ));
  }
 
  @override
  Widget build(BuildContext context) {
    if (user == null) {
      return const Scaffold(
        body: Center(
          child: Text('No user data available'),
        ),
      );
    }
    DescopeUser userInfo = user!;
 
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('Hi, ${userInfo.name}!',
                textAlign: TextAlign.center,
                style: Theme.of(context).textTheme.titleLarge),
            Text('Email: ${userInfo.email}'),
            Text('Phone: ${userInfo.phone}'),
            const SizedBox(height: 20),
            CupertinoButton(
              color: Theme.of(context).primaryColorDark,
              onPressed: _logout,
              child: const Text('Log out'),
            )
          ],
        ),
      ),
    );
  }
}

Install Backend SDK

Install the SDK by including the SDK in your pom.xml file (for installation via Maven).

pom.xml
<dependency>
	<artifactId>java-sdk</artifactId>
	<groupId>com.descope</groupId>
	<version>1.0.25</version>
</dependency>

Import and Setup Backend SDK

You'll need import and setup all of the packages from the SDK.

If you're using a CNAME with your Descope project, make sure to export the Base URL (e.g. export DESCOPE_BASE_URI="https://api.descope.com") when initializing descope_client.

Application.java
package com.descope.java_sample_app;
 
// descope imports start
import com.descope.client.*;
import com.descope.exception.DescopeException;
import com.descope.model.jwt.Token;
import com.descope.sdk.auth.AuthenticationService;
// descope imports end
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RestController;
 
@SpringBootApplication
@RestController
public class JavaSampleAppApplication {
 
	public static void main(String[] args) {
		SpringApplication.run(JavaSampleAppApplication.class, args);
	}
 
}

Implement Session Validation

You will need to then fetch the session token from the Authorization header of each request, and use the SDK to validate the token.

The frontend SDK will store the session token in either a cookie or your browser's local storage. If using a cookie, the token will be sent to your app server automatically with every request.

Application.java
@SpringBootApplication
@RestController
public class JavaSampleAppApplication {
 
	public static void main(String[] args) {
		SpringApplication.run(JavaSampleAppApplication.class, args);
	}
	
	public void validateSession(String sessionToken, String refreshToken) {
		var descopeClient = new DescopeClient(Config.builder().projectId("__ProjectID__").build());
 
		// Validate the session. Will return an error if expired
		AuthenticationService as = descopeClient.getAuthenticationServices().getAuthService();
		try {
			Token t = as.validateSessionWithToken(sessionToken);
		} catch (DescopeException de) {
			// Handle the unauthorized error
		}
 
		// If ValidateSessionWithRequest raises an exception, you will need to refresh the session using
		try {
			Token t = as.refreshSessionWithToken(refreshToken);
		} catch (DescopeException de) {
			// Handle the unauthorized error
		}
 
		// Alternatively, you could combine the two and
		// have the session validated and automatically refreshed when expired
		try {
			Token t = as.validateAndRefreshSessionWithTokens(sessionToken, refreshToken);
		} catch (DescopeException de) {
			// unauthorized error
		}
	}
	
}

Congratulations

Now that you've got the authentication down, go focus on building out the rest of your app!


Checkpoint

Your application is now integrated with Descope. Please test with sign-up or sign-in use case.

Need help?

Customize

Now that you have the end-to-end application working, you can choose to configure and personalize many different areas of Descope, including your brand, style, custom user authentication journeys, etc. We recommend starting with customizing your user-facing screens, such as signup and login.

Was this helpful?

On this page