Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't get web auth to work with my own authentication server #154

Open
JPFrancoia opened this issue Apr 20, 2023 · 6 comments
Open

Can't get web auth to work with my own authentication server #154

JPFrancoia opened this issue Apr 20, 2023 · 6 comments

Comments

@JPFrancoia
Copy link

Hello,

I have my own authentication service to authenticate my users. The service is written in Go. The authentication flow works like this:

  • the client queries http://localhost:8080/auth/facebook
  • the client is redirected to a facebook page where the user will authorize my app
  • facebook then redirects to http://localhost:8080/auth/callback with the user info (I do stuff with this data, like adding the user to a database)

The above works. I'm now trying to build the UI. So far I have something like this:

import 'package:flutter/material.dart';
import 'package:social_login_buttons/social_login_buttons.dart';
import 'package:flutter_web_auth/flutter_web_auth.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Sample',
      home: LoginPage(),
    );
  }
}

class LoginPage extends StatelessWidget {
  LoginPage({super.key});

  @override
  Widget build(BuildContext context) {

    return LayoutBuilder(builder: (context, constraints) {
      return Scaffold(
        body: SocialLoginButton(
          buttonType: SocialLoginButtonType.facebook,
          onPressed: () async {
            final result = await FlutterWebAuth.authenticate(
                url: "http://localhost:8080/auth/facebook", callbackUrlScheme: "callback-scheme");

            print("result: $result");

            final token = await Uri.parse(result).queryParameters['token'];
            print(token);
          },
        ),
      );
    });
  }
}

On the server side, this is the implementation of the /auth/callback endpoint:

func AuthCallback(c *gin.Context) {
	user, err := gothic.CompleteUserAuth(c.Writer, c.Request)

        ...

	c.HTML(http.StatusOK, "auth.html", gin.H{})
	// c.Redirect(http.StatusFound, "callback-scheme://")
}

I tried serving the auth.html page mentioned here from the server. With this setup, I can click the Facebook button, the web page opens, I can login on Facebook, and I can give permissions to the app. I'm then redirected to the auth.html page, and then...nada. I never get back anything for final result =... (on the client side). There is no error in the flutter console.

I tried other stuff on the server side:

	c.JSON(http.StatusOK, gin.H{"token": "hello"})

With this, I ultimately a json blob with the hello token in the web page that opens, but I'm not getting anything back on the client side.

I'm missing something, but I don't know what. Would you be able to help please?

@LinusU
Copy link
Owner

LinusU commented Apr 20, 2023

The auth.html page is if you are using Flutter Web and is not for iOS/Android.

c.Redirect(http.StatusFound, "callback-scheme://")

This looks like the correct approach to me, could it possibly be that there needs to be something more in the url? Could you try changing this to something like:

c.Redirect(http.StatusFound, "callback-scheme://success")

Also, which platform are you targeting?

@JPFrancoia
Copy link
Author

The auth.html page is if you are using Flutter Web

Yes, I understood that. For now I'm testing the setup with chrome, so I went for the web approach, but I'm not getting anything back for result.

Also, which platform are you targeting?

Ideally, all of them :)

I'm going to try on android just now (it's not as easy as the web approach because of the networking)

@LinusU
Copy link
Owner

LinusU commented Apr 20, 2023

The auth.html page is if you are using Flutter Web

Yes, I understood that. For now I'm testing the setup with chrome, so I went for the web approach, but I'm not getting anything back for result.

Ah, I see. Unfortunately I haven't personally tested the web setup much, it was contributed by someone else. Could you try the example app in this repo and see if that works for you?

@JPFrancoia
Copy link
Author

Ok it also doesn't work for Android (we can focus on this platform for now if you have worked with it more).

Server:

func AuthCallback(c *gin.Context) {
	user, err := gothic.CompleteUserAuth(c.Writer, c.Request)

        ...

	c.Redirect(http.StatusFound, "callback-scheme://success")
}

Client:

            final result = await FlutterWebAuth.authenticate(
                url: url, callbackUrlScheme: "callback-scheme");

            print("result: $result");

What I see on the phone:

  • clicking the login button redirects to Facebook, in some sort of web view window
  • On Facebook, I can authorize the app
  • I'm still in the webview at this point. I can see the url being https://mysite/auth/callback, but the page is blank and nothing is happening at this point

I'm not sure how it should work here, if auth/callback redirects to callback-scheme://success, where do I send the JWT token back?

Should I do something like this on the server side:

func AuthCallback(c *gin.Context) {
	user, err := gothic.CompleteUserAuth(c.Writer, c.Request)

        ...
	// httpOnly MUST be set to true for security
	c.SetCookie(
		"refresh_token",
		refreshToken,
		int(VALIDITY_REFRESH_TOKEN.Seconds()),
		"/",
		HOST,
		false,
		true,
	)
	c.Redirect(http.StatusFound, "callback-scheme://success")
}

@JPFrancoia
Copy link
Author

Ok I got it working on Android. My mistake was to generate the oauth URL and returning it as a json payload, instead of just redirecting to the url:

func Login(c *gin.Context) {
        ...
	url, err := gothic.GetAuthURL(c.Writer, c.Request)

	if err != nil {
		c.AbortWithError(http.StatusInternalServerError, err)
	}

	// c.JSON(http.StatusOK, gin.H{"redirect": url}) // NOT GOOD
	c.Redirect(http.StatusFound, url)
}

The rest is unchanged.

Now unfortunately this doesn't work with web as is.

@JPFrancoia
Copy link
Author

Could you try the example app in this repo and see if that works for you?

I tried it. I don't think it works. First, I needed to run with flutter run -d web-server --web-port=43823 --web-hostname=127.0.0.1, because otherwise I'm getting the error Error: Unsupported operation: ServerSocket.bind. This is because the example app uses dart:io, which isn't available in web (see: https://stackoverflow.com/questions/62935688/unsupported-operation-at-serversocket-bind-using-httpserver-in-flutter)

I got it to run (no error, authentication page displaying properly in browser), when I click the auth button I'm redirected to the expected url but I don't see the result of result = await FlutterWebAuth.authenticate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants