How to implement Bearer Token
what is x-play-integrity
Difference and Role of the Two Tokens
Flutter - Secure API base URL & Other URL's
How to Enable Play Integrity API in playstore
Flutter Frontend (Full Example)
a) Login and Store Token
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
// Login, receive token, and store in SharedPreferences
Future<void> loginUser(String email, String password) async {
final url = 'https://yourdomain.com/api/login';
final response = await http.post(
Uri.parse(url),
headers: {"Content-Type": "application/json"},
body: jsonEncode({
"email": email,
"password": password,
}),
);
if (response.statusCode == 200) {
final decoded = jsonDecode(response.body);
final token = decoded['token']; // <-- Adjust key if your response is different
// Store token securely
final prefs = await SharedPreferences.getInstance();
await prefs.setString('accessToken', token);
print('Login successful, token stored');
} else {
throw Exception('Login failed: ${response.body}');
}
}
pacticalway
String token = responseData['token'].toString(); // Extract email
// Save email securely
await _saveEmailCredentials(email,token);
Future<bool> verify_otp(String phone, String otp, BuildContext context) async {
final prefs = await SharedPreferences.getInstance();
String? dialCode = prefs.getString("dial_code");
if (dialCode == null || dialCode.isEmpty) {
print("Dial code is missing in SharedPreferences");
return false; // Handle case where dial code is not set
}
// Concatenate dial code with phone (without + sign)
String combinedPhone = "$dialCode$phone";
var url = "$baseUrl/api/verify_otp";
var body = jsonEncode({"phone": combinedPhone, "otp": otp});
print("Request URL: $url");
print("Request Body: $body");
try {
final response = await http.post(
Uri.parse(url),
headers: {"Content-Type": "application/json"},
body: body,
);
print("Response verify_otp Status Code: ${response.statusCode}");
print("Response Body verify_otp: ${response.body}");
if (response.statusCode == 200) {
var responseData = jsonDecode(response.body);
// Check if the response indicates success
if (responseData['success'] == true) {
String email = responseData['user']['email'].toString(); // Extract email
print("Email received: $email");
String token = responseData['token'].toString(); // Extract email
// Save email securely
await _saveEmailCredentials(email,token);
print("Phone and OTP saved securely!");
return true; // Indicate success
} else {
print("Invalid response format or OTP missing");
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Register(phone: combinedPhone),
),
);
return false;
}
} else {
print("Failed request with status: ${response.statusCode}");
return false;
}
} catch (e) {
print("Exception: $e");
throw Exception("An error occurred while processing the login request");
}
}
Use Stored Token For Authenticated API Requests
Future<String?> getToken() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('accessToken');
}
Future<List<AddVehicle>> getVehicleData() async {
final url = 'https://yourdomain.com/api/getmyvehicle';
final token = await getToken();
final response = await http.post(
Uri.parse(url),
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer $token", // Secure header
},
body: jsonEncode({}),
);
print("Response Status: ${response.statusCode}");
if (response.statusCode == 200) {
List<dynamic> data = jsonDecode(response.body)['data'];
return data.map((json) => AddVehicle.fromJson(json)).toList();
} else {
throw Exception('Failed: ${response.body}');
}
}
practical way
- Laravel Backend
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Api\AuthController;
use App\Http\Controllers\Api\PartnerController;
// Public route(s)
Route::post('login', [AuthController::class, 'login']);
// Protected routes
Route::middleware('auth:sanctum')->group(function () {
Route::post('getmyvehicle', [PartnerController::class, 'getmyvehicle']);
// Add all other protected routes here...
});
practical way
AuthController: Issue a Token
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\User; // Adjust namespace/Model as needed
use Illuminate\Support\Facades\Hash;
class AuthController extends Controller
{
public function login(Request $request)
{
$user = User::where('email', $request->email)->first();
if ($user && Hash::check($request->password, $user->password)) {
$token = $user->createToken('mobile')->plainTextToken;
return response()->json([
'token' => $token,
'user' => $user,
]);
} else {
return response()->json(['error' => 'Invalid credentials'], 401);
}
}
}
practical way
Example PartnerController with Auth
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class PartnerController extends Controller
{
public function getmyvehicle(Request $request)
{
$user = $request->user();
// Example: send all vehicles owned by user
$vehicles = $user->vehicles; // Adjust relation/model as needed
return response()->json([
'data' => $vehicles,
]);
}
}
======================================================
C:\Project\motoshare_partner\android\app\src\main\kotlin\com\example\motoshare_partner\MainActivity.kt
package com.cotocus.motoshare.partner
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import com.google.android.play.core.integrity.IntegrityManagerFactory
import com.google.android.play.core.integrity.IntegrityTokenRequest
class MainActivity : FlutterActivity() {
private val CHANNEL = "play_integrity_channel"
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
call, result ->
if (call.method == "getPlayIntegrityToken") {
val nonce = call.argument<String>("nonce") ?: ""
getPlayIntegrityToken(nonce, result)
}
}
}
private fun getPlayIntegrityToken(nonce: String, result: MethodChannel.Result) {
val integrityManager = IntegrityManagerFactory.create(this)
val request = IntegrityTokenRequest.builder().setNonce(nonce).build()
integrityManager.requestIntegrityToken(request)
.addOnSuccessListener { response ->
result.success(response.token())
}
.addOnFailureListener { e ->
result.error("INTEGRITY_ERROR", e.localizedMessage, null)
}
}
}
step2
C:\Project\motoshare_partner\android\app\build.gradle
implementation 'com.google.android.play:integrity:1.3.0'
step3
C:\Project\motoshare_partner\lib\Service\Influencer\motoshare_service.dart
import 'dart:convert';
import 'dart:math';
import 'package:flutter/services.dart';
const platform = MethodChannel('play_integrity_channel');
Future<String?> getPlayIntegrityToken(String nonce) async {
try {
return await platform.invokeMethod<String>('getPlayIntegrityToken', {'nonce': nonce});
} on PlatformException catch (e) {
print("Failed to get Play Integrity token: $e");
return null;
}
}
String generateWebSafeNonce([int length = 32]) {
final random = Random.secure();
final bytes = List<int>.generate(length, (_) => random.nextInt(256));
return base64UrlEncode(bytes); // Web-safe, no-wrap!
}
Future<List<AddVehicle>> getVehicleData(String email) async {
var url = "$baseUrl/api/getVehicleData";
var body = jsonEncode({"email": email});
final token = await getToken();
print("Request URL: $url");
print("Request Body: $body");
final nonce = generateWebSafeNonce(); // 32 bytes
final integrityToken = await getPlayIntegrityToken(nonce);
print("intigrity token is");
print(integrityToken);
final response = await http.post(
Uri.parse(url),
headers: {"Content-Type": "application/json",
"Authorization": "Bearer $token",
},
body: body,
);
print("Response Status: ${response.statusCode}");
print("Response Body getVehicleData: ${response.body}");
if (response.statusCode == 200) {
List<dynamic> body = jsonDecode(response.body)['data']; // Access 'data' key
return body.map((vehicleData) => AddVehicle.fromJson(vehicleData)).toList();
} else {
throw Exception("Failed to fetch vehicle data, status: ${response.statusCode}");
}
}
restart emulator
step4:
Send Integrity Token With Your API Request
Modify your API call to include the integrity token
Future<List<AddVehicle>> getVehicleData(String integrityToken, String authToken) async {
var url = "$baseUrl/api/getmyvehicle";
final response = await http.post(
Uri.parse(url),
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer $authToken", // your login/session token (as before)
"X-PLAY-INTEGRITY": integrityToken, // NEW: add Play Integrity token
},
body: jsonEncode({}),
);
print("Response Status: ${response.statusCode}");
print("Response Body: ${response.body}");
if (response.statusCode == 200) {
List<dynamic> body = jsonDecode(response.body)['data'];
return body.map((vehicleData) => AddVehicle.fromJson(vehicleData)).toList();
} else {
throw Exception("Failed to fetch vehicle data, status: ${response.statusCode}");
}
Difference and Role of the Two Tokens
Is there a way a random public internet user can access your API?
Restrict Routes in routes/api.php
Route::middleware(['auth:sanctum', 'play.integrity'])->group(function() {
Route::post('getprofile', [PartnerController::class, 'getprofile']);
// ...all protected routes
});
=================or==================
Route::middleware(['play.integrity'])->group(function () {
Route::post('guest-only-action', [PartnerController::class, 'guestOnlyAction']);
// Add other "public but real app only" routes here
});
Middleware: Play Integrity Only
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
class PlayIntegrityMiddleware
{
public function handle(Request $request, Closure $next)
{
$token = $request->header('X-PLAY-INTEGRITY');
if (!$token) {
return response()->json(['error' => 'Missing Integrity Token'], 401);
}
$apiKey = config('services.play_integrity.api_key');
$packageName = config('services.play_integrity.package_name');
// Replace with Google Play Integrity API call for production
$response = Http::withHeaders([
'Authorization' => "Bearer $apiKey",
])->post(
"https://playintegrity.googleapis.com/v1/$packageName:decodeIntegrityToken",
[
'integrity_token' => $token
]
);
if ($response->failed() || !isset($response['tokenPayloadExternal'])) {
return response()->json(['error' => 'Integrity check failed'], 401);
}
$payload = $response['tokenPayloadExternal'];
// Only allow requests from your real app on Play Store
if (
$payload['appIntegrity']['appRecognitionVerdict'] !== 'PLAY_RECOGNIZED' ||
$payload['appIntegrity']['packageName'] !== $packageName
) {
return response()->json(['error' => 'Untrusted app environment'], 401);
}
return $next($request);
}
}
=====================or====================
// app/Http/Middleware/CheckPlayIntegrity.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
class CheckPlayIntegrity
{
public function handle(Request $request, Closure $next)
{
$token = $request->header('X-PLAY-INTEGRITY');
if (!$token) {
return response()->json(['error' => 'No Integrity Token'], 401);
}
// Verify the token with Google Play backend
$apiKey = config('services.play_integrity.api_key');
$packageName = config('services.play_integrity.package_name');
$response = Http::withHeaders([
'Authorization' => "Bearer $apiKey",
])->post(
"https://playintegrity.googleapis.com/v1/$packageName:decodeIntegrityToken",
[
'integrity_token' => $token
]
);
if ($response->failed() || !isset($response['tokenPayloadExternal'])) {
return response()->json(['error' => 'Integrity check failed'], 401);
}
$payload = $response['tokenPayloadExternal'];
// Example: Ensure app is installed from Google Play and is unmodified
if (
$payload['appIntegrity']['appRecognitionVerdict'] !== 'PLAY_RECOGNIZED' ||
$payload['appIntegrity']['packageName'] !== $packageName
) {
return response()->json(['error' => 'Untrusted app environment'], 401);
}
return $next($request);
}
}
'play.integrity' => \App\Http\Middleware\CheckPlayIntegrity::class,
How to Enable Play Integrity API in playstore
when play integrity is not setup it is written
integrity not started
By enabling Play Integrity API responses in the Google Play Console, you will gain access to additional configuration options, testing features, and API reporting. This option is only available to apps distributed on Google Play. Navigate to Release > App integrity. Under Play Integrity API select Link a Cloud project
. Choose the Cloud project you want to link to your app and this will enable Play Integrity API responses. You can now integrate the Play Integrity API into your app.
Step1 click link cloud project
step2:click link project
now it is written integration started
Flutter - Secure API base URL & Other URL's
https://www.youtube.com/watch?v=aewc3rYocWY
Step-by-Step: How to Get $apiKey (Play Integrity Server API Key)
Create a Service Account in Google Cloud Platform
Go to Google Cloud Console
Select or create the project that matches your Android app in Google Play Console.
In the left menu, go to IAM & admin > Service accounts
Click Create Service Account
Provide a name, then Create and continue
Grant the service account the role "Play Integrity API User"
Download the Service Account JSON Key
After creating the account, click on it in the list.
Go to the Keys tab.
Choose Add Key > Create new key > JSON
Download the JSON key file (keep this file secure!).
Place this file somewhere secure on your server, such as storage/app/play-integrity-service-account.json or follow Google’s recommended paths ([reference]).
Grant API Access to Play Integrity API
In your Google Cloud project, enable the Play Integrity API (search for it in the APIs & Services Library).
Make sure your app in the Play Console is linked to this cloud project ([reference]).
Set Environment Variable or Laravel Config
You do not copy a single string token into your .env, instead, you reference the downloaded JSON key file for authentication.
In your .env:
GOOGLE_APPLICATION_CREDENTIALS=/path/to/your/play-integrity-service-account.json
PLAY_INTEGRITY_PACKAGE_NAME=com.yourcompany.yourapp
In config/services.php (example):
'play_integrity' => [
'json_credentials' => env('GOOGLE_APPLICATION_CREDENTIALS'),
'package_name' => env('PLAY_INTEGRITY_PACKAGE_NAME'),
],
error
8): Failed to get Play Integrity token: PlatformException(INTEGRITY_ERROR, -1: Integrity API error (-1): Integrity API is not available.
I/flutter ( 4308): Integrity API is not enabled, or the Play Store version might be old.
I/flutter ( 4308): Recommended actions:
I/flutter ( 4308): 1) Make sure that Integrity API is enabled in Google Play Console.
I/flutter ( 4308): 2) Ask the user to update Play Store.
I/flutter ( 4308): (https://developer.android.com/google/play/integrity/reference/com/google/android/play/core/integrity/model/IntegrityErrorCode.html#API_NOT_AVAILABLE)., null, null)
Solution:
Top comments (0)