Skip to content

Commit 16e7a5a

Browse files
committed
Update github_login.dart
1 parent 7c35e1c commit 16e7a5a

File tree

1 file changed

+123
-0
lines changed

1 file changed

+123
-0
lines changed

lib/src/github_login.dart

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import 'dart:io';
2+
3+
import 'package:flutter/material.dart';
4+
import 'package:http/http.dart' as http;
5+
import 'package:oauth2/oauth2.dart' as oauth2;
6+
import 'package:url_launcher/url_launcher.dart';
7+
8+
final _authorizationEndpoint =
9+
Uri.parse('https://github.com/login/oauth/authorize');
10+
final _tokenEndpoint = Uri.parse('https://github.com/login/oauth/access_token');
11+
12+
class GithubLoginWidget extends StatefulWidget {
13+
const GithubLoginWidget({
14+
required this.builder,
15+
required this.githubClientId,
16+
required this.githubClientSecret,
17+
required this.githubScopes,
18+
super.key,
19+
});
20+
final AuthenticatedBuilder builder;
21+
final String githubClientId;
22+
final String githubClientSecret;
23+
final List<String> githubScopes;
24+
25+
@override
26+
State<GithubLoginWidget> createState() => _GithubLoginState();
27+
}
28+
29+
typedef AuthenticatedBuilder = Widget Function(
30+
BuildContext context, oauth2.Client client);
31+
32+
class _GithubLoginState extends State<GithubLoginWidget> {
33+
HttpServer? _redirectServer;
34+
oauth2.Client? _client;
35+
36+
@override
37+
Widget build(BuildContext context) {
38+
final client = _client;
39+
if (client != null) {
40+
return widget.builder(context, client);
41+
}
42+
43+
return Scaffold(
44+
appBar: AppBar(
45+
title: const Text('Github Login'),
46+
),
47+
body: Center(
48+
child: ElevatedButton(
49+
onPressed: () async {
50+
await _redirectServer?.close();
51+
// Bind to an ephemeral port on localhost
52+
_redirectServer = await HttpServer.bind('localhost', 0);
53+
var authenticatedHttpClient = await _getOAuth2Client(
54+
Uri.parse('http://localhost:${_redirectServer!.port}/auth'));
55+
setState(() {
56+
_client = authenticatedHttpClient;
57+
});
58+
},
59+
child: const Text('Login to Github'),
60+
),
61+
),
62+
);
63+
}
64+
65+
Future<oauth2.Client> _getOAuth2Client(Uri redirectUrl) async {
66+
if (widget.githubClientId.isEmpty || widget.githubClientSecret.isEmpty) {
67+
throw const GithubLoginException(
68+
'githubClientId and githubClientSecret must be not empty. '
69+
'See `lib/github_oauth_credentials.dart` for more detail.');
70+
}
71+
var grant = oauth2.AuthorizationCodeGrant(
72+
widget.githubClientId,
73+
_authorizationEndpoint,
74+
_tokenEndpoint,
75+
secret: widget.githubClientSecret,
76+
httpClient: _JsonAcceptingHttpClient(),
77+
);
78+
var authorizationUrl =
79+
grant.getAuthorizationUrl(redirectUrl, scopes: widget.githubScopes);
80+
81+
await _redirect(authorizationUrl);
82+
var responseQueryParameters = await _listen();
83+
var client =
84+
await grant.handleAuthorizationResponse(responseQueryParameters);
85+
return client;
86+
}
87+
88+
Future<void> _redirect(Uri authorizationUrl) async {
89+
if (await canLaunchUrl(authorizationUrl)) {
90+
await launchUrl(authorizationUrl);
91+
} else {
92+
throw GithubLoginException('Could not launch $authorizationUrl');
93+
}
94+
}
95+
96+
Future<Map<String, String>> _listen() async {
97+
var request = await _redirectServer!.first;
98+
var params = request.uri.queryParameters;
99+
request.response.statusCode = 200;
100+
request.response.headers.set('content-type', 'text/plain');
101+
request.response.writeln('Authenticated! You can close this tab.');
102+
await request.response.close();
103+
await _redirectServer!.close();
104+
_redirectServer = null;
105+
return params;
106+
}
107+
}
108+
109+
class _JsonAcceptingHttpClient extends http.BaseClient {
110+
final _httpClient = http.Client();
111+
@override
112+
Future<http.StreamedResponse> send(http.BaseRequest request) {
113+
request.headers['Accept'] = 'application/json';
114+
return _httpClient.send(request);
115+
}
116+
}
117+
118+
class GithubLoginException implements Exception {
119+
const GithubLoginException(this.message);
120+
final String message;
121+
@override
122+
String toString() => message;
123+
}

0 commit comments

Comments
 (0)