platform/packages/auth_oauth2/example/main.dart

130 lines
4.2 KiB
Dart
Raw Normal View History

2017-06-03 20:43:36 +00:00
import 'dart:convert';
import 'package:angel3_auth/angel3_auth.dart';
import 'package:angel3_framework/angel3_framework.dart';
import 'package:angel3_framework/http.dart';
import 'package:angel3_auth_oauth2/angel3_auth_oauth2.dart';
2019-01-06 00:43:06 +00:00
import 'package:http_parser/http_parser.dart';
2018-03-30 16:44:15 +00:00
import 'package:logging/logging.dart';
2019-01-06 00:43:06 +00:00
var authorizationEndpoint =
Uri.parse('http://github.com/login/oauth/authorize');
2017-06-03 20:43:36 +00:00
2019-01-06 00:43:06 +00:00
var tokenEndpoint = Uri.parse('https://github.com/login/oauth/access_token');
2017-06-03 20:43:36 +00:00
2019-01-06 00:43:06 +00:00
var options = ExternalAuthOptions(
clientId: '6caeaf5d4c04936ec34f',
clientSecret: '178360518cf9de4802e2346a4b6ebec525dc4427',
redirectUri: Uri.parse('http://localhost:3000/auth/github/callback'),
);
/// Github doesn't properly follow the OAuth2 spec, so here's logic to parse their response.
Map<String, dynamic> parseParamsFromGithub(MediaType contentType, String body) {
if (contentType.type == 'application') {
2021-03-13 00:05:59 +00:00
if (contentType.subtype == 'x-www-form-urlencoded') {
2019-01-06 00:43:06 +00:00
return Uri.splitQueryString(body);
2021-03-13 00:05:59 +00:00
} else if (contentType.subtype == 'json') {
2019-01-06 00:43:06 +00:00
return (json.decode(body) as Map).cast<String, String>();
2021-03-13 00:05:59 +00:00
}
2019-01-06 00:43:06 +00:00
}
2018-03-30 16:44:15 +00:00
2019-01-06 00:43:06 +00:00
throw FormatException(
'Invalid content-type $contentType; expected application/x-www-form-urlencoded or application/json.');
}
2021-03-13 00:05:59 +00:00
void main() async {
2019-01-06 00:43:06 +00:00
// Create the server instance.
var app = Angel();
var http = AngelHttp(app);
app.logger = Logger('angel')
..onRecord.listen((rec) {
print(rec);
if (rec.error != null) print(rec.error);
if (rec.stackTrace != null) print(rec.stackTrace);
});
2017-06-03 20:43:36 +00:00
2019-01-06 00:43:06 +00:00
// Create a service that stores user data.
var userService = app.use('/users', MapService()).inner;
var mappedUserService = userService.map(User.parse, User.serialize);
2017-06-03 20:43:36 +00:00
2019-01-06 00:43:06 +00:00
// Set up the authenticator plugin.
2021-07-10 01:45:50 +00:00
var auth = AngelAuth<User>(
serializer: (user) async => user.id,
deserializer: (id) => mappedUserService.read(id.toString()),
jwtKey: 'oauth2 example secret',
allowCookie: false);
await app.configure(auth.configureServer);
2019-01-06 00:43:06 +00:00
/// Create an instance of the strategy class.
auth.strategies['github'] = OAuth2Strategy(
options,
authorizationEndpoint,
tokenEndpoint,
// This function is called when the user ACCEPTS the request to sign in with Github.
(client, req, res) async {
2021-03-13 00:05:59 +00:00
var response = await client.get(Uri.parse('https://api.github.com/user'));
2018-09-12 03:23:42 +00:00
var ghUser = json.decode(response.body);
var id = ghUser['id'] as int?;
2018-09-12 03:23:42 +00:00
2019-01-06 00:43:06 +00:00
var matchingUsers = await mappedUserService.index({
'query': {'github_id': id}
2018-09-12 03:23:42 +00:00
});
if (matchingUsers.isNotEmpty) {
2019-01-06 00:43:06 +00:00
// Return the corresponding user, if it exists.
return matchingUsers.first;
2018-09-12 03:23:42 +00:00
} else {
// Otherwise,create a user
2019-01-06 00:43:06 +00:00
return await mappedUserService.create(User(githubId: id));
2018-09-12 03:23:42 +00:00
}
},
2019-01-06 00:43:06 +00:00
// This function is called when an error occurs, or the user REJECTS the request.
(e, req, res) async {
res.write('Ooops: $e');
await res.close();
},
// We have to pass this parser function when working with Github.
//getParameters: parseParamsFromGithub,
2018-09-12 03:23:42 +00:00
);
2017-06-03 20:43:36 +00:00
2019-01-06 00:43:06 +00:00
// Mount some routes
2017-06-03 20:43:36 +00:00
app.get('/auth/github', auth.authenticate('github'));
app.get(
'/auth/github/callback',
auth.authenticate('github',
2019-01-06 00:43:06 +00:00
AngelAuthOptions(callback: (req, res, jwt) async {
2017-06-03 20:43:36 +00:00
// In real-life, you might include a pop-up callback script.
//
// Use `confirmPopupAuthentication`, which is bundled with
// `package:angel_auth`.
var user = req.container!.make<User>()!;
2019-01-06 00:43:06 +00:00
res.write('Your user info: ${user.toJson()}\n\n');
2017-06-03 20:43:36 +00:00
res.write('Your JWT: $jwt');
2019-01-06 00:43:06 +00:00
await res.close();
2017-06-03 20:43:36 +00:00
})));
2019-01-06 00:43:06 +00:00
// Start listening.
await http.startServer('127.0.0.1', 3000);
print('Listening on ${http.uri}');
print('View user listing: ${http.uri}/users');
print('Sign in via Github: ${http.uri}/auth/github');
2017-06-03 20:43:36 +00:00
}
class User extends Model {
@override
String? id;
2019-01-06 00:43:06 +00:00
int? githubId;
2017-06-03 20:43:36 +00:00
User({this.id, this.githubId});
static User parse(Map map) =>
User(id: map['id'] as String?, githubId: map['github_id'] as int?);
2019-01-06 00:43:06 +00:00
static Map<String, dynamic> serialize(User user) => user.toJson();
2017-06-03 20:43:36 +00:00
Map<String, dynamic> toJson() => {'id': id, 'github_id': githubId};
}