Log-in users in flutter through social accounts with laravel-socialite backend

1,855

Solution 1

I have solved it, after some digging I found out Laravel-Socialite has the functionality to log in users using their token built-in:

Quoting Socialite documentation:

If you already have a valid access token for a user, you can retrieve their details using Socialite's userFromToken method.

Solution 2

Apparently, google sign in doesn't work on flutter except with Firebase/some cloud API backend service. I was using a local Laravel API for user auth so adding google sign in functionality requires setting up a firebase account/profile, downloading and adding the googleservices.json file to flutter project as explained in google_sign_in package installation manual. You also need to import firebase-auth package

Flutter Code (I use flutter modular pattern but same applies with Bloc/Provider if you get the idea as explained by Hamza Mogni above)

import 'package:google_sign_in/google_sign_in.dart';
 import 'package:firebase_auth/firebase_auth.dart';

  final GoogleSignIn _googleSignIn = GoogleSignIn();
  final FirebaseAuth _auth = FirebaseAuth.instance;

  Future<LoginResponseModel> googleLoginResponse() async {
    
    String url = env['API_BASE_URL'] + '/api/auth/google';

    //click on google sign in. Get accessToken from google through googlesignin 
     plugin.
    //Send accessToken to socialite in backend to request/create user data

    GoogleSignInAccount googleSignInAccount = await _googleSignIn.signIn();
    if (googleSignInAccount == null) {
      print('Google Signin ERROR! googleAccount: null!');
      return null;
    }
    GoogleSignInAuthentication googleSignInAuthentication =
        await googleSignInAccount.authentication;
    
   //this is user access token from google that is retrieved with the plugin
    print("User Access Token: ${googleSignInAuthentication.accessToken}");
    String accessToken = googleSignInAuthentication.accessToken;
    
    //make http request to the laravel backend 
    final response =
        await http.post(
                  url,
                  body: json.encode({"token": accessToken}),
                  headers: {"Content-Type": "application/json"});
    if (response.statusCode == 200 || response.statusCode == 422) {
      return LoginResponseModel.fromJson(
        json.decode(response.body), // {'message':'Google signin successful'}
      );
    } else {
      throw Exception('Failed to load data!');
    }
  }

For Logout function, you need to signout of both firebase and google account instance or you will always be logged in by the first known/used google account in subsequent login attempts.

Future<LogoutResponseModel> logout() async {
    try {
      await _auth.signOut();
      await _googleSignIn.disconnect();
    } catch (e) {
      print('Failed to sign out ' + e.toString());
    }
    //api route to destroy sanctum token. santum token is added as authorization header
    var url = env['API_BASE_URL'] + "/api/logout";
    final response =
        await http.post(Uri.tryParse(url), headers: {'Bearer ' $sanctumtoken});
    if (response.statusCode == 200 || response.statusCode == 422) {
      return LogoutResponseModel.fromJson(
        json.decode(response.body),
      );
    } else {
      throw Exception('Failed to load data!');
    }
  }

Laravel Code (route to controller method is api/auth/google, method expects to receive google access token from flutter app)

public function requestTokenGoogle(Request $request) {
        // Getting the user from socialite using token from google
        $user = Socialite::driver('google')->stateless()->userFromToken($request->token);

        // Getting or creating user from db
        $userFromDb = User::firstOrCreate(
            ['email' => $user->getEmail()],
            [
                'email_verified_at' => now(),
                'first_name' => $user->offsetGet('given_name'),
                'last_name' => $user->offsetGet('family_name'),
                'avatar' => $user->getAvatar(),
            ]
        );

        // Returning response
        $token = $userFromDb->createToken('Laravel Sanctum Client')->plainTextToken;
        $response = ['token' => $token, 'message' => 'Google Login/Signup Successful'];
        return response($response, 200);
    }
Share:
1,855
Hamza Mogni
Author by

Hamza Mogni

Updated on December 26, 2022

Comments

  • Hamza Mogni
    Hamza Mogni over 1 year

    I am working on a flutter application, and I want to implement social login (Google and Facebook).

    My API is implemented with Laravel and uses Laravel-socialite to authenticate users, there is the backend, web frontend (using VueJs) and now I am working on the mobile application using flutter.

    The web application is working good (using the vue-social-auth package).

    What I have done till now:

    1. Used flutter_google_sign_in to handle authentication on the flutter app.
    2. Did configure the package and I can successfully get user info through that package.

    Problem I am facing:

    What I don't seem to get working is to send the user that just logged in to the backend in order to provide an in-app user experience.

    This is what the vue-social-auth package provides and what I send to the backend, which is working fine:

    {code: "4/0AY0e-g442SMxdtLb_MVdQ63u1ydp48bbCRQco5Azoyf3y1rvYybDabyZGOvwAs7ZFJDQHA", scope: "email+profile+openid+https://www.googleapis.com/au…le+https://www.googleapis.com/auth/userinfo.email", authuser: "0", prompt: "consent"}
    

    And this is what flutter_google_sign_in gives (aside of the user profile data:

    idToken: "",
    accessToken: "",
    serverAuthCode: "", 
    

    serverAuthCode is always null.

    How can I make it so that, using the same API logic, log-in users on flutter through social accounts?

    Thank you.