Git Product home page Git Product logo

supertokens-flutter's Introduction

SuperTokens Flutter SDK

chat on Discord

About

This is a Flutter SDK written in pure dart that is responsible for maintaining a SuperTokens session for a Flutter app.

Learn more at https://supertokens.com

Usage

Initialise the SDK

import 'package:supertokens_flutter/supertokens.dart';

// Initialise the SDK on app launch
void main() {
    SuperTokens.init(apiDomain: "http://localhost:3001");
}

Checking if a session exists

import 'package:supertokens_flutter/supertokens.dart';

Future<bool> doesSessionExist() async {
    return await SuperTokens.doesSessionExist();
}

Usage with http

Making network requests

You can make requests as you normally would with http, the only difference is that you import the client from the supertokens package instead.

// Import http from the SuperTokens package
import 'package:supertokens_flutter/http.dart' as http;

Future<void> makeRequest() {
    Uri uri = Uri.parse("http://localhost:3001/api");
    var response = await http.get(uri);
    // handle response
}

The SuperTokens SDK will handle session expiry and automatic refreshing for you. When calling authentication APIs such as signin or signup, the SDK automatically captures the access- and refresh tokens from the headers and saves them for you.

Using a custom http Client

If you use a custom http client and want to use SuperTokens, you can simply provide the SDK with your client. All requests will continue to use your client along with the session logic that SuperTokens provides.

// Import http from the SuperTokens package
import 'package:supertokens_flutter/http.dart' as http;

Future<void> makeRequest() {
    Uri uri = Uri.parse("http://localhost:3001/api");

    // provide your custom client to SuperTokens
    var httpClient = http.Client(client: customClient)

    var response = await httpClient.get(uri);
    // handle response
}

Usage with Dio

Add the SuperTokens interceptor

You can make requests as you normally would with dio.

import 'package:supertokens_flutter/dio.dart';

void setup() {
    Dio dio = Dio(...)
    dio.interceptors.add(SuperTokensInterceptorWrapper(client: dio));
}

Or use instance method instead.

import 'package:supertokens_flutter/dio.dart';

void setup() {
  Dio dio = Dio();  // Create a Dio instance.
  dio.addSupertokensInterceptor();
}

Making network requests

import 'package:supertokens_flutter/dio.dart';

void setup() {
    Dio dio = Dio(...)
    dio.interceptors.add(SuperTokensInterceptorWrapper(client: dio));

    var response = dio.get("http://localhost:3001/api");
    // handle response
}

Signing out

import 'package:supertokens_flutter/supertokens.dart';

Future<void> signOut() async {
    await SuperTokens.signOut();
}

Getting the user id

import 'package:supertokens_flutter/supertokens.dart';

Future<String> getUserId() async {
    return await SuperTokens.getUserId();
}

Manually refresh sessions

import 'package:supertokens_flutter/supertokens.dart';

Future<void> manualRefresh() async {
    // Returns true if session was refreshed, false if session is expired
    var success = await SuperTokens.attemptRefreshingSession();
}

Contributing

Please refer to the CONTRIBUTING.md file in this repo.

Contact us

For any queries, or support requests, please email us at [email protected], or join our Discord server.

Authors

Created with ❤️ by the folks at SuperTokens.com.

supertokens-flutter's People

Contributors

daniilborovoy avatar dwaynecoussement avatar iresharma avatar lille-morille avatar nkshah2 avatar rishabhpoddar avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

supertokens-flutter's Issues

Research Points

  1. Flutter platform channels vs Pure dart
  2. Dio vs Http
  • Interception
  • Popularity
  • Cookie management
  • Cookie persistence
  1. CICD - Codemagic vs Travis

Session does not exist

I'm able to send and verify an SMS OTP in my Flutter app, but I'm not able to get any session info on subsequent calls. Also, I'm using the HTTP Client and it does not inject a the Authentication header on requests to my backend.

What am I missing?

Desktop support

Would like to have support for Flutter desktop applications

Shortcomings and issues that prevent easy migration from Firebase

I am porting our Flutter multiplatform applications (linux, web, android) from firebase to supertokens based authentication. During the process of porting I found several problems and was forced to sacrifice some of the existing user account management functionality. Here I have tried to summarize the difficulties I encountered in the hope that some of them will be addressed by the supertokens_flutter team in the future.

  1. The supertokens_flutter package is built on the assumption that all applications use HTTP to communicate not only with the supertokens server, but also with their own business logic backends. Since our apps use grpc to communicate with our backends, the supertokens_flutter cannot be used as intended, e.g. as a magical wrapper around the http communication package that hides details such as session refresh, access token insertion, etc. Initially, I had doubts whether I should use this package at all, but currently I came up with a solution in which I reuse few of the publicly available properties and functions like init(), attemptRefreshingSession(), etc. We cannot use the supertokens frontend either, because it would create discrepancies in the look and feel, and also damage apps support for theming, internationalization, etc.

In order for the supertokens_flutter package to be useful for use cases similar to ours, the current API needs to be extended to allow subscribing to authentication state changes as well as user profile and access token changes, similar to what the Firebase Auth API for Flutter provides.

  1. The supertokens_flutter package uses shared preferences to persist some sensitive data, but our applications use a different storage implementation. It would be nice if a storage API was defined so that people could interface their own storage implementation. That way, their applications can store their preferences in a single store, which could eventually implement ecryption.

  2. The timeJoined field is only included in responses to signup or signin calls. If the applications just resume the authentication session on startup instead of forcing the user to re-signin, then there seems to be no way to display this value on the user profile page. The result of the getAccessTokenPayloadSecurely() function call does not contain timeJoined, and I have not been able to find any other Supertokens API call besides signup and signin that would allow me to get the user's timeJoined property.

  3. Unlike Firebase authentication, there is no out-of-the-box support for the displayName of the user account. see firebase get user data API call

  4. Unlike Firebase authentication, there is no out-of-the-box support for the passwordUpdatedAt of the user account. see firebase get user data API call

  5. Unlike Firebase authentication, there is no out-of-the-box support for the lastLoginAt of the user account. see firebase get user data API call

  6. Unlike Firebase authentication, there is no out-of-the-box support for the photoUrl of the user account. see firebase get user data API call

Screenshot_User_Profile_2

As you can see from the screenshot of the Firebase powered user profile page of our apps, most of the information and functionality cannot be easily implemented with Supertokens.

getAccessToken does not work

Hi,

I keep getting a 401 with my API, so I checked the respective variables once.
The function getAccessToken doesn't seem to work, as it returns null even though a session exists.

The code:

      var hasSession = await SuperTokens.doesSessionExist();
      print('hasSession');
      print(hasSession);
      var accessToken = await SuperTokens.getAccessToken();
      print('accessToken');
      print(accessToken);

Result:

I/flutter (17793): hasSession
I/flutter (17793): true
I/flutter (17793): accessToken
I/flutter (17793): null

Thanks for your work!

Unpin `http` version

http was pinned down without an explanation why:

http: "<1.2.0"

This also pins down google_sign_in_dartio to a version < 0.3 and trying to upgrade it leads to this issue:

> flutter pub get
Resolving dependencies... 
Because no versions of cross_file match >0.3.4+1 <0.4.0 and cross_file 0.3.4+1 depends on web ^0.5.0, cross_file ^0.3.4+1 requires web ^0.5.0.
And because http >=1.1.2 <1.2.1 depends on web >=0.3.0 <0.5.0, cross_file ^0.3.4+1 is incompatible with http >=1.1.2 <1.2.1.
And because google_sign_in_dartio >=0.2.3 depends on http ^1.1.2 and supertokens_flutter 0.3.1 depends on http <1.2.0, one of cross_file ^0.3.4+1 or google_sign_in_dartio >=0.2.3 or
  supertokens_flutter 0.3.1 must be false.
And because no versions of supertokens_flutter match >0.3.1 <0.4.0 and my_project depends on cross_file ^0.3.4+1, google_sign_in_dartio >=0.2.3 is incompatible with
  supertokens_flutter ^0.3.1.
So, because my_project depends on both supertokens_flutter ^0.3.1 and google_sign_in_dartio ^0.3.0, version solving failed.


You can try the following suggestion to make the pubspec resolve:
* Consider downgrading your constraint on google_sign_in_dartio: flutter pub add google_sign_in_dartio:'^0.2.2+1'

What is the reason for pinning the http package down? Can it be unpinned?

`dio` version update

Hello,
Are there any plans to update dio? The dio version is 4.0.0 which is 2 years old, due to which we are unable to use latest versions for other dependencies in our codebase.
I would like to help contribute if that's possible.
Thank you.

Updating sdk with new Supertokens APIs

Findings

During our testing with the default http client (dart: http package) we figured out internally the http package calls the http.send(Request) function, where in the get, put, post, etc methods create the appropriate Request object and pass it down to the send which finally makes the network request.

We tested this by extending the base client and overriding the send function to print a string and call the send function from the super class, then we called the get, post, put, methods from the new class. This resulted in still printing the string.

import 'package:http/http.dart' as http;

class CustomClient extends http.BaseClient {
  late final http.Client _inner;

  Future<http.StreamedResponse> send(http.BaseRequest request) {
    print('send method called');
    return _inner.send(request);
  }
}
var client = CustomClient();
Uri uri = Uri.parse("localhost:5000/");
client.get(uri);

Proposed Solution

In the above test we simply created an object of the custom client, but we want to write our SDK in complete parity with the official http client. Hence we will architect the sdk in a way that we also export the functions required to send requests. Along with get, post, put,etc. Function we'll also export the custom client which will give the user access to the following functions to better interact with the API.

Along with a few functions specific to supertokens API, these functions being

  • signOut()
  • getUserId()
  • doesSessionExists()
  • getTokenPayloadSecurely()
  • attemptRefreshingSession()

Here's what using the http package to make a get request looks like.

import 'package:http/http.dart' as http;

void main() async {
  Uri url = Uri.parse('https://try.supertokens.com');
  var resp = await http.get(url);
}

The following is what you need to do to make replicate similar behaviour

import 'package:supertokens/supertokens-http-client.dart' as http;

void main() async {
  Uri url = Uri.parse('https://try.supertokens.com');
  var resp = await http.get(url);
}

As you can see the only change is the import statement, you don't need to change anything else, we follow similar rules with the client as well.

Flow of the new SDK

Proposed SDK follows this flow:

Initial step requires initiate the Supertoken class

Suupertokens.init({
    required String apiDomain,  // Domain where the supertokens backend resides
    String? apiBasePath,  // path for the authentication endpoints
    int sessionExpiredStatusCode = 401,  // http Status code for session expire response
    String? cookieDomain,  
    Function(Eventype)? eventHandler,   // voidCallback ran for various events (events listed below)
    Function(APIAction, http.Request)? preAPIHook,  // callback ran before sending the request to supertokens
    Function(APIAction, http.Request, http.Response)? postAPIHook,  // callback ran after response received from supertokens
})

** Event types for eventHandler

enum Eventype {
  SIGN_OUT,
  REFRESH_SESSION,
  SESSION_CREATED,
  ACCESS_TOKEN_PAYLOAD_UPDATED,
  UNAUTHORISED
}

This can be followed by checking if a session exists or not

Supertokens.doesSessionExist();

Following this we can import the supertokens http client and use the http functions as is

import 'package:supertokens/supertokens-http-client.dart' as http;

void main() async {
  Uri url = Uri.parse('https://try.supertokens.com');
  var resp = await http.get(url);
}

Dio adaptation for the SDK

Unlike the http library dio allows us to use interceptors, hence that's what we'll be leveraging to implement out SDk.

Dio addInterceptors(Dio dio) {
  return dio..interceptors.add(InterceptorsWrapper(
      onRequest: (RequestOptions options) => requestInterceptor(options),
      onResponse: (Response response) => responseInterceptor(response),
      onError: (DioError dioError) => errorInterceptor(dioError)));
}

This is how we add interceptors to dio, so these callbacks onRequest, onResponse and onError can be exported from the Supertokens class.

Then the above will look something like this:

Dio addInterceptors(Dio dio) {
  return dio..interceptors.add(SuperTokensInterceptorWrapper());
}

apart from this we tend to make the cookieStore a singleton and share this across both the methods this helps us in a major way because we already have a lot of functions already written in http hence by sharing the cookieStore we can only expose interceptors to do the checks and modifying things to cookie but the signOut, refresh and other internet functions can still be done by using the http based functions.

Dio 4.0.6 doesn't work

SuperTokensInterceptorWrapper doesn't work.

Error: Bad state future already completed.

Issues when api-domain and auth-domain are on different ports

You provide the possibility to have different backends for the api-endpoints and auth-endpoints, by configuring sessionTokenBackendDomain. In our environment the two systems run on the same domain, but on different ports.

When you try to authenticate your Dio api-requests via SuperTokensInterceptorWrapper, the method shouldRunDioInterceptor will return false, when the ports differ, leaving the requests unauthenticated. The causing code is:

    if (SuperTokensUtils.getApiDomain(options.uri.toString()) !=
        SuperTokens.config.apiDomain) {
      return false;
    }

In my example the two values would evaluate to:

  • SuperTokensUtils.getApiDomain(options.uri.toString()) -> https://example.com:65000/ (which is the api-backend)
  • SuperTokens.config.apiDomain) -> https://example.com:3000/ (which is the auth-backend)

Using the following config:

    SuperTokens.init(
        apiDomain: 'https://example.com:3000/',
        sessionTokenBackendDomain: '.example.com/');

This issue prevents the sessionTokenBackendDomain from being evaluated. Wouldn't it be sufficient to rely on the following check only?

    if (!Utils.shouldDoInterceptions(
        options.uri.toString(),
        SuperTokens.config.apiDomain,
        SuperTokens.config.sessionTokenBackendDomain)) {
      return false;
    }

Refused to set unsafe header "cookie"

Setup is passwordless (OTP token).

Supertokens-postgres 7.0.16
Backend - NestJS.
Frontend - 2 apps:

  1. Angular using supertokens-web-js
  2. Flutter web using supertokens_flutter

Everything seems to be working as it should, but I have "Refused to set unsafe header "cookie"" error on each request from flutter web to backend.

flutter api service:

import 'package:supertokens_flutter/http.dart' as superhttp;

...

final response = await superhttp.post(
  uri,
  headers: {
    'Content-type': 'application/json',
    'rid': 'passwordless',
  },
  body: json.encode(body),
);

Screenshot 2024-01-07 at 12 19 51

Web support

Would like to have support for Flutter web applications

SuperTokens.init throws exception for api base path

Hi
I initialize supertokens with

SuperTokens.init(
      apiDomain: "http://localhost:8081",
      apiBasePath: 'auth',
      tokenTransferMethod: SuperTokensTokenTransferMethod.HEADER);

and catch the exception

SuperTokensException (Invalid protocol)

Callstack:

NormalisedURLPath.normaliseIRLPathOrThrowError (pub.dev/supertokens_flutter-0.2.7/lib/src/normalised-url-path.dart:15)
new NormalisedURLPath (pub.dev/supertokens_flutter-0.2.7/lib/src/normalised-url-path.dart:7)
new NormalisedInputType.normaliseInputType (pub.dev/supertokens_flutter-0.2.7/lib/src/utilities.dart:375)
SuperTokens.init (pub.dev/supertokens_flutter-0.2.7/lib/src/supertokens.dart:55)
main (<mymain>)
<asynchronous gap> (Unknown Source:0)

in factory NormalisedInputType.normaliseInputType() is the call

var _apiBasePath = NormalisedURLPath("/auth");

that throws in normaliseIRLPathOrThrowError at:

try {
      if (!trimmedInput.startsWith('http'))
        throw SuperTokensException('Invalid protocol');

SDK: supertokens_flutter: ^0.2.4

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.