Finalized OAuth flow, added /profile/{LOGIN} routes, improved home_view with a background
This commit is contained in:
BIN
assets/images/cluster-photo-00.jpg
Normal file
BIN
assets/images/cluster-photo-00.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 MiB |
@@ -17,11 +17,30 @@ class MyApp extends StatelessWidget {
|
|||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
title: '42 API Client',
|
title: '42 API Client',
|
||||||
initialRoute: '/',
|
initialRoute: '/',
|
||||||
routes: {
|
onGenerateRoute: (settings) {
|
||||||
'/': (context) => const InitScreen(),
|
final uri = Uri.parse(settings.name ?? '/');
|
||||||
'/home': (context) => HomeScreen(),
|
|
||||||
'/login': (context) => const LoginScreen(),
|
if (uri.path == '/') {
|
||||||
'/profile': (context) => const ProfileScreen(),
|
return MaterialPageRoute(builder: (_) => const InitScreen());
|
||||||
|
}
|
||||||
|
if (uri.path == '/home') {
|
||||||
|
return MaterialPageRoute(builder: (_) => const HomeScreen());
|
||||||
|
}
|
||||||
|
if (uri.path == '/login') {
|
||||||
|
return MaterialPageRoute(builder: (_) => const LoginScreen());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uri.pathSegments.length == 2 && uri.pathSegments[0] == 'profile') {
|
||||||
|
final login = uri.pathSegments[1];
|
||||||
|
return MaterialPageRoute(
|
||||||
|
builder: (_) => ProfileScreen(login: login),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return MaterialPageRoute(
|
||||||
|
builder: (_) => const Scaffold(
|
||||||
|
body: Center(child: Text('404 - Page not found')),
|
||||||
|
),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,24 @@
|
|||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:http/http.dart';
|
||||||
|
|
||||||
Future<bool> checkToken() async
|
Future<bool> checkToken() async
|
||||||
{
|
{
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
return prefs.containsKey('user_token');
|
if (prefs.containsKey('user_token') == false) { return false; }
|
||||||
|
final date = DateTime.fromMillisecondsSinceEpoch(prefs.getInt('expiration') ?? 0);
|
||||||
|
return date.isAfter(DateTime.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveToken(String token) async
|
// Future<Map<String, dynamic>> fetchProfileData(String login) async
|
||||||
|
// {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
Future<void> saveToken(String token, String refresh, DateTime expiration) async
|
||||||
{
|
{
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
await prefs.setString('user_token', token);
|
await prefs.setString('user_token', token);
|
||||||
|
await prefs.setString('refresh_token', refresh);
|
||||||
|
await prefs.setInt('expiration', expiration.millisecondsSinceEpoch);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String?> getToken() async
|
Future<String?> getToken() async
|
||||||
|
|||||||
@@ -1,20 +1,69 @@
|
|||||||
// home_screen.dart
|
// home_screen.dart
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class HomeScreen extends StatelessWidget {
|
class HomeScreen extends StatefulWidget {
|
||||||
const HomeScreen({super.key});
|
const HomeScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<HomeScreen> createState() => _HomeScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _HomeScreenState extends State<HomeScreen> {
|
||||||
|
final TextEditingController _controller = TextEditingController();
|
||||||
|
|
||||||
|
void _handleInput(BuildContext context, String input) {
|
||||||
|
// Replace with your desired logic
|
||||||
|
print("Input received: $input");
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('You entered: $input')),
|
||||||
|
);
|
||||||
|
Navigator.pushReplacementNamed(context, '/profile/${input}');
|
||||||
|
}
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: const Text("Home")),
|
appBar: AppBar(title: const Text("Home")),
|
||||||
body: Center(
|
body: Stack(
|
||||||
child: ElevatedButton(
|
fit: StackFit.expand,
|
||||||
onPressed: () {
|
children: [
|
||||||
Navigator.pushNamed(context, '/profile');
|
Image.asset(
|
||||||
},
|
'assets/images/cluster-photo-00.jpg',
|
||||||
child: const Text('Go to Profile'),
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
|
Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
TextField(
|
||||||
|
controller: _controller,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: 'Enter something...',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
filled: true,
|
||||||
|
fillColor: Colors.white70,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
_handleInput(context, _controller.text);
|
||||||
|
},
|
||||||
|
child: const Text('Submit Input'),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pushNamed(context, '/profile');
|
||||||
|
},
|
||||||
|
child: const Text('Go to Profile'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_appauth/flutter_appauth.dart';
|
import 'package:flutter_appauth/flutter_appauth.dart';
|
||||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||||
|
import 'package:swifty/methods/api.dart';
|
||||||
|
|
||||||
const FlutterAppAuth _appAuth = FlutterAppAuth();
|
const FlutterAppAuth _appAuth = FlutterAppAuth();
|
||||||
|
|
||||||
Future<void> redirect_to_oauth(BuildContext context) async {
|
Future<void> redirect_to_oauth(BuildContext context) async {
|
||||||
final String _clientId = dotenv.env['CLIENT-ID'] ?? '';
|
final String _clientId = "CLIENT TODO";
|
||||||
final String _clientSecret = dotenv.env['CLIENT-SECRET'] ?? '';
|
final String _clientSecret = "SECRET TODO";
|
||||||
final String _redirectUrl = 'swifty-companion://oauth2/callback';
|
final String _redirectUrl = 'swifty-companion://oauth2/callback';
|
||||||
final String _authorizationEndpoint = 'https://api.intra.42.fr/oauth/authorize';
|
final String _authorizationEndpoint = 'https://api.intra.42.fr/oauth/authorize';
|
||||||
final String _tokenEndpoint = 'https://api.intra.42.fr/oauth/token';
|
final String _tokenEndpoint = 'https://api.intra.42.fr/oauth/token';
|
||||||
@@ -18,36 +20,30 @@ Future<void> redirect_to_oauth(BuildContext context) async {
|
|||||||
authorizationEndpoint: _authorizationEndpoint,
|
authorizationEndpoint: _authorizationEndpoint,
|
||||||
tokenEndpoint: _tokenEndpoint,
|
tokenEndpoint: _tokenEndpoint,
|
||||||
));
|
));
|
||||||
print("swap");
|
|
||||||
try {
|
try {
|
||||||
print("trying");
|
|
||||||
final AuthorizationResponse? result = await _appAuth.authorize(request);
|
final AuthorizationResponse? result = await _appAuth.authorize(request);
|
||||||
print("tried");
|
if (result == null) { throw PlatformException(code: "fatal_error"); }
|
||||||
if (result != null) {
|
final token_request = TokenRequest(
|
||||||
print("nonull result");
|
_clientId,
|
||||||
Navigator.pushReplacementNamed(context, "/home");
|
_redirectUrl,
|
||||||
print('Authorization Code: ${result.authorizationCode}');
|
clientSecret: _clientSecret,
|
||||||
final token_request = TokenRequest(
|
authorizationCode: result.authorizationCode,
|
||||||
_clientId,
|
grantType: 'authorization_code',
|
||||||
_redirectUrl,
|
serviceConfiguration: AuthorizationServiceConfiguration(
|
||||||
clientSecret: _clientSecret,
|
authorizationEndpoint: "https://api.intra.42.fr/oauth/authorize",
|
||||||
authorizationCode: result.authorizationCode,
|
tokenEndpoint: _tokenEndpoint,
|
||||||
grantType: 'authorization_code',
|
),
|
||||||
serviceConfiguration: AuthorizationServiceConfiguration(
|
);
|
||||||
authorizationEndpoint: "https://api.intra.42.fr/oauth/authorize",
|
final TokenResponse? tokenResponse = await _appAuth.token(token_request);
|
||||||
tokenEndpoint: _tokenEndpoint,
|
saveToken(tokenResponse?.accessToken ?? '', tokenResponse?.refreshToken ?? '', tokenResponse?.accessTokenExpirationDateTime ?? DateTime.now());
|
||||||
),
|
Navigator.pushReplacementNamed(context, "/home");
|
||||||
);
|
}
|
||||||
print("pre token");
|
on PlatformException catch (e)
|
||||||
final TokenResponse? tokenResponse = await _appAuth.token(token_request);
|
{
|
||||||
print("token");
|
if (e.code == "authorize_failed" && e.message?.contains('User cancelled flow') == true) { return; }
|
||||||
print(tokenResponse?.accessToken);
|
|
||||||
print(tokenResponse?.refreshToken);
|
|
||||||
// You can now use this code to exchange for an access token (via a separate API call to the token endpoint)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
print("error $e");
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class LoginScreen extends StatelessWidget {
|
class LoginScreen extends StatelessWidget {
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class ProfileScreen extends StatelessWidget {
|
class ProfileScreen extends StatelessWidget {
|
||||||
const ProfileScreen({super.key});
|
final String login;
|
||||||
|
const ProfileScreen({super.key, required this.login});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -11,9 +12,9 @@ class ProfileScreen extends StatelessWidget {
|
|||||||
body: Center(
|
body: Center(
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.pushNamed(context, '/');
|
Navigator.pushNamed(context, '/login');
|
||||||
},
|
},
|
||||||
child: const Text('Go to Home'),
|
child: Text('Go to ${login}'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ dev_dependencies:
|
|||||||
flutter:
|
flutter:
|
||||||
assets:
|
assets:
|
||||||
- assets/images/42_logo.png
|
- assets/images/42_logo.png
|
||||||
|
- assets/images/cluster-photo-00.jpg
|
||||||
# The following line ensures that the Material Icons font is
|
# The following line ensures that the Material Icons font is
|
||||||
# included with your application, so that you can use the icons in
|
# included with your application, so that you can use the icons in
|
||||||
# the material Icons class.
|
# the material Icons class.
|
||||||
|
|||||||
Reference in New Issue
Block a user