2017-06-03 20:43:36 +00:00
|
|
|
import 'dart:convert';
|
2021-05-30 00:46:13 +00:00
|
|
|
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);
|
2021-05-30 00:46:13 +00:00
|
|
|
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.
|
2021-05-30 00:46:13 +00:00
|
|
|
//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`.
|
2021-05-30 00:46:13 +00:00
|
|
|
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
|
2021-05-30 00:46:13 +00:00
|
|
|
String? id;
|
2019-01-06 00:43:06 +00:00
|
|
|
|
2021-05-30 00:46:13 +00:00
|
|
|
int? githubId;
|
2017-06-03 20:43:36 +00:00
|
|
|
|
|
|
|
User({this.id, this.githubId});
|
|
|
|
|
|
|
|
static User parse(Map map) =>
|
2021-05-30 00:46:13 +00:00
|
|
|
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};
|
|
|
|
}
|