Skip to content

Commit 1de8864

Browse files
authored
🎉 Add Web support (#77)
Fixes #16
1 parent 0028e2a commit 1de8864

10 files changed

+177
-2
lines changed

README.md

+23-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
A Flutter plugin for authenticating a user with a web service, even if the web service is run by a third party. Most commonly used with OAuth2, but can be used with any web flow that can redirect to a custom scheme.
44

5-
In the background, this plugin uses [`ASWebAuthenticationSession`][ASWebAuthenticationSession] on iOS 12+ and macOS 10.15+, [`SFAuthenticationSession`][SFAuthenticationSession] on iOS 11, and [Chrome Custom Tabs][Chrome Custom Tabs] on Android. You can build it with iOS 8+, but it is currently only supported by iOS 11 or higher.
5+
In the background, this plugin uses [`ASWebAuthenticationSession`][ASWebAuthenticationSession] on iOS 12+ and macOS 10.15+, [`SFAuthenticationSession`][SFAuthenticationSession] on iOS 11, [Chrome Custom Tabs][Chrome Custom Tabs] on Android and opens a new window on Web. You can build it with iOS 8+, but it is currently only supported by iOS 11 or higher.
66

77
[ASWebAuthenticationSession]: https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession
88
[SFAuthenticationSession]: https://developer.apple.com/documentation/safariservices/sfauthenticationsession
@@ -70,7 +70,7 @@ final accessToken = jsonDecode(response.body)['access_token'] as String;
7070

7171
## Setup
7272

73-
Setup works as for any Flutter plugin, expect the Android caveat outlined below:
73+
Setup works as for any Flutter plugin, expect the Android and Web caveats outlined below:
7474

7575
### Android
7676

@@ -92,3 +92,24 @@ In order to capture the callback url, the following `activity` needs to be added
9292
</application>
9393
</manifest>
9494
```
95+
96+
### Web
97+
98+
On the Web platform an endpoint needs to be created that captures the callback URL and sends it to the application using the JavaScript `postMessage()` method. In the `./web` folder of the project, create an HTML file with the name e.g. `auth.html` with content:
99+
100+
```html
101+
<!DOCTYPE html>
102+
<title>Authentication complete</title>
103+
<p>Authentication is complete. If this does not happen automatically, please
104+
close the window.
105+
<script>
106+
window.opener.postMessage({
107+
'flutter-web-auth': window.location.href
108+
}, window.location.origin);
109+
window.close();
110+
</script>
111+
```
112+
113+
Redirection URL passed to the authentication service must be the same as the URL on which the application is running (schema, host, port if necessary) and the path must point to created HTML file, `/auth.html` in this case. The `callbackUrlScheme` parameter of the `authenticate()` method does not take into account, so it is possible to use a schema for native platforms in the code.
114+
115+
For the Sign in with Apple in web_message response mode, postMessage from https://appleid.apple.com is also captured, and the authorization object is returned as a URL fragment encoded as a query string (for compatibility with other providers).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//
2+
// Generated file. Do not edit.
3+
//
4+
5+
// ignore_for_file: lines_longer_than_80_chars
6+
7+
import 'package:flutter_web_auth/src/flutter_web_auth_web.dart';
8+
9+
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
10+
11+
// ignore: public_member_api_docs
12+
void registerPlugins(Registrar registrar) {
13+
FlutterWebAuthPlugin.registerWith(registrar);
14+
registrar.registerMessageHandler();
15+
}

example/web/auth.html

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!DOCTYPE html>
2+
<title>Authentication complete</title>
3+
<p>Authentication is complete. If this does not happen automatically, please
4+
close the window.
5+
<script>
6+
window.opener.postMessage({
7+
'flutter-web-auth': window.location.href
8+
}, window.location.origin);
9+
window.close();
10+
</script>

example/web/favicon.png

917 Bytes
Loading

example/web/icons/Icon-192.png

5.17 KB
Loading

example/web/icons/Icon-512.png

8.06 KB
Loading

example/web/index.html

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<!--
5+
If you are serving your web app in a path other than the root, change the
6+
href value below to reflect the base path you are serving from.
7+
8+
The path provided below has to start and end with a slash "/" in order for
9+
it to work correctly.
10+
11+
Fore more details:
12+
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
13+
-->
14+
<base href="/">
15+
16+
<meta charset="UTF-8">
17+
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
18+
<meta name="description" content="A new Flutter project.">
19+
20+
<!-- iOS meta tags & icons -->
21+
<meta name="apple-mobile-web-app-capable" content="yes">
22+
<meta name="apple-mobile-web-app-status-bar-style" content="black">
23+
<meta name="apple-mobile-web-app-title" content="example">
24+
<link rel="apple-touch-icon" href="icons/Icon-192.png">
25+
26+
<!-- Favicon -->
27+
<link rel="icon" type="image/png" href="favicon.png"/>
28+
29+
<title>example</title>
30+
<link rel="manifest" href="manifest.json">
31+
</head>
32+
<body>
33+
<!-- This script installs service_worker.js to provide PWA functionality to
34+
application. For more information, see:
35+
https://developers.google.com/web/fundamentals/primers/service-workers -->
36+
<script>
37+
if ('serviceWorker' in navigator) {
38+
window.addEventListener('flutter-first-frame', function () {
39+
navigator.serviceWorker.register('flutter_service_worker.js');
40+
});
41+
}
42+
</script>
43+
<script src="main.dart.js" type="application/javascript"></script>
44+
</body>
45+
</html>

example/web/manifest.json

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "example",
3+
"short_name": "example",
4+
"start_url": ".",
5+
"display": "standalone",
6+
"background_color": "#0175C2",
7+
"theme_color": "#0175C2",
8+
"description": "A new Flutter project.",
9+
"orientation": "portrait-primary",
10+
"prefer_related_applications": false,
11+
"icons": [
12+
{
13+
"src": "icons/Icon-192.png",
14+
"sizes": "192x192",
15+
"type": "image/png"
16+
},
17+
{
18+
"src": "icons/Icon-512.png",
19+
"sizes": "512x512",
20+
"type": "image/png"
21+
}
22+
]
23+
}

lib/src/flutter_web_auth_web.dart

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import 'dart:async';
2+
import 'dart:convert';
3+
import 'dart:html';
4+
import 'dart:js';
5+
6+
import 'package:flutter/services.dart';
7+
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
8+
9+
class FlutterWebAuthPlugin {
10+
static void registerWith(Registrar registrar) {
11+
final MethodChannel channel = MethodChannel(
12+
'flutter_web_auth', const StandardMethodCodec(), registrar.messenger);
13+
final FlutterWebAuthPlugin instance = FlutterWebAuthPlugin();
14+
channel.setMethodCallHandler(instance.handleMethodCall);
15+
}
16+
17+
Future<dynamic> handleMethodCall(MethodCall call) async {
18+
switch (call.method) {
19+
case 'authenticate':
20+
final String url = call.arguments['url'];
21+
return _authenticate(url);
22+
default:
23+
throw PlatformException(
24+
code: 'Unimplemented',
25+
details: "The flutter_web_auth plugin for web doesn't implement "
26+
"the method '${call.method}'");
27+
}
28+
}
29+
30+
static Future<String> _authenticate(String url) async {
31+
context.callMethod('open', [url]);
32+
await for (MessageEvent messageEvent in window.onMessage) {
33+
if (messageEvent.origin == Uri.base.origin) {
34+
final flutterWebAuthMessage = messageEvent.data['flutter-web-auth'];
35+
if (flutterWebAuthMessage is String) {
36+
return flutterWebAuthMessage;
37+
}
38+
}
39+
var appleOrigin = Uri(scheme: 'https', host: 'appleid.apple.com');
40+
if (messageEvent.origin == appleOrigin.toString()) {
41+
try {
42+
Map<String, dynamic> data = jsonDecode(messageEvent.data);
43+
if (data['method'] == 'oauthDone') {
44+
final appleAuth = data['data']['authorization'];
45+
if (appleAuth != null) {
46+
final appleAuthQuery = Uri(queryParameters: appleAuth).query;
47+
return appleOrigin.replace(fragment: appleAuthQuery).toString();
48+
}
49+
}
50+
} on FormatException {}
51+
}
52+
}
53+
throw new PlatformException(
54+
code: 'error', message: 'Iterable window.onMessage is empty');
55+
}
56+
}

pubspec.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ environment:
1010
dependencies:
1111
flutter:
1212
sdk: flutter
13+
flutter_web_plugins:
14+
sdk: flutter
1315

1416
dev_dependencies:
1517
flutter_test:
@@ -33,6 +35,9 @@ flutter:
3335
pluginClass: FlutterWebAuthPlugin
3436
macos:
3537
pluginClass: FlutterWebAuthPlugin
38+
web:
39+
pluginClass: FlutterWebAuthPlugin
40+
fileName: src/flutter_web_auth_web.dart
3641

3742
# To add assets to your plugin package, add an assets section, like this:
3843
# assets:

0 commit comments

Comments
 (0)