Skip to content

Commit

Permalink
Merge pull request #69 from javad-zobeidi/dev
Browse files Browse the repository at this point in the history
Refactor Authentication class
  • Loading branch information
javad-zobeidi committed May 30, 2024
2 parents cd649c6 + 00df029 commit d59b118
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 51 deletions.
2 changes: 1 addition & 1 deletion lib/src/authentication/authenticate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class Authenticate extends Middleware {
} else {
await Auth().guard(guard!).check(token ?? '');
}
next?.handle(req);
return next?.handle(req);
} on JWTExpiredException {
throw Unauthenticated(message: 'Token expired');
}
Expand Down
107 changes: 65 additions & 42 deletions lib/src/authentication/authentication.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,26 +38,36 @@ class Auth {

dynamic get(String filed) => _user[_userGuard][filed];

Future<Map<String, dynamic>> createToken(
{Duration? expiresIn, bool withRefreshToken = false}) async {
Future<Map<String, dynamic>> createToken({
Duration? expiresIn,
bool withRefreshToken = false,
bool customToken = false,
}) async {
Map<String, dynamic> token = HasApiTokens()
.setPayload(_user[_userGuard])
.createToken(_userGuard, expiresIn, withRefreshToken);

await PersonalAccessTokens().query().insert({
'name': _userGuard,
'tokenable_id': _user[_userGuard]['id'],
'token': md5.convert(utf8.encode(token['access_token'])),
'created_at': DateTime.now(),
});
if (!customToken) {
await PersonalAccessTokens().query().insert({
'name': _userGuard,
'tokenable_id': _user[_userGuard]['id'],
'token': md5.convert(utf8.encode(token['access_token'])),
'created_at': DateTime.now(),
});
}

return token;
}

Map<String, dynamic> createTokenByRefreshToken(String token,
{Duration? expiresIn}) {
return HasApiTokens()
.refreshToken(token.replaceFirst('Bearer ', ''), _userGuard, expiresIn);
Map<String, dynamic> createTokenByRefreshToken(
String token, {
Duration? expiresIn,
}) {
return HasApiTokens().refreshToken(
token.replaceFirst('Bearer ', ''),
_userGuard,
expiresIn,
);
}

Future<bool> deleteTokens() async {
Expand All @@ -77,43 +87,56 @@ class Auth {
return true;
}

Future<bool> check(String token, {Map<String, dynamic>? user}) async {
Future<bool> check(
String token, {
Map<String, dynamic>? user,
bool isCustomToken = false,
}) async {
Map<String, dynamic> payload = HasApiTokens()
.verify(token.replaceFirst('Bearer ', ''), _userGuard, 'access_token');

Map<String, dynamic>? exists = await PersonalAccessTokens()
.query()
.where('token', '=', md5.convert(utf8.encode(token)))
.whereNull('deleted_at')
.first(['id']);
// Throw 401 Error if token not found
if (exists == null) {
throw Unauthenticated(message: 'Invalid token');
}

await PersonalAccessTokens()
.query()
.where('token', '=', md5.convert(utf8.encode(token)))
.update({'last_used_at': DateTime.now()});

if (user == null) {
Model? authenticatable =
Config().get('auth')['guards'][_userGuard]['provider'];

if (authenticatable == null) {
throw InvalidArgumentException('Authenticatable class not found');
}
user =
await authenticatable.query().where('id', '=', payload['id']).first();
}

if (user != null) {
_user[_userGuard] = user;
if (isCustomToken) {
_user[_userGuard] = payload;
_isAuthorized = true;
_currentToken = token;
return true;
} else {
throw Unauthenticated(message: 'Invalid token');
Map<String, dynamic>? exists = await PersonalAccessTokens()
.query()
.where('token', '=', md5.convert(utf8.encode(token)))
.whereNull('deleted_at')
.first(['id']);
// Throw 401 Error if token not found
if (exists == null) {
throw Unauthenticated(message: 'Invalid token');
}

await PersonalAccessTokens()
.query()
.where('token', '=', md5.convert(utf8.encode(token)))
.update({'last_used_at': DateTime.now()});

if (user == null) {
Model? authenticatable =
Config().get('auth')['guards'][_userGuard]['provider'];

if (authenticatable == null) {
throw InvalidArgumentException('Authenticatable class not found');
}
user = await authenticatable
.query()
.where('id', '=', payload['id'])
.first();
}

if (user != null) {
_user[_userGuard] = user;
_isAuthorized = true;
_currentToken = token;
return true;
} else {
throw Unauthenticated(message: 'Invalid token');
}
}
}
}
39 changes: 31 additions & 8 deletions lib/src/authentication/has_api_tokens.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,26 +22,39 @@ class HasApiTokens {
Duration? expiresIn,
bool withRefreshToken = false,
]) {
String secretKey = env('JWT_SECRET_KEY') ?? env<String>('APP_KEY');
Map<String, dynamic> userId = {'id': _userPayload?['id']};
if (_userPayload?['id'] == null) {
userId = {'_id': _userPayload?['_id']};
}

final jwt = JWT(
{
'id': _userPayload?['id'],
'user': jsonEncode(_userPayload),
'type': 'access_token',
...userId,
},
audience: env('JWT_AUDIENCE') == null
? null
: Audience.one(env<String>('JWT_AUDIENCE')),
jwtId: env<String?>('JWT_ID'),
issuer: env<String?>('JWT_ISSUER'),
subject: env<String?>('JWT_SUBJECT'),
);
Map<String, dynamic> payload = {};
Duration expirationTime = expiresIn ?? const Duration(hours: 1);

String accessToken = jwt.sign(SecretKey('${env('APP_KEY')}$guard'),
expiresIn: expirationTime);
String accessToken =
jwt.sign(SecretKey('$secretKey$guard'), expiresIn: expirationTime);

payload['access_token'] = accessToken;

if (withRefreshToken) {
final jwtRefresh =
JWT({'id': _userPayload?['id'], 'type': 'refresh_token'});
String refreshToken = jwtRefresh.sign(
SecretKey('${env('APP_KEY')}$guard'),
final jwtRefresh = JWT({
...userId,
'type': 'refresh_token',
});
String refreshToken = jwtRefresh.sign(SecretKey('$secretKey$guard'),
expiresIn: const Duration(days: 30));
payload['refresh_token'] = refreshToken;
}
Expand All @@ -65,8 +78,18 @@ class HasApiTokens {

// Verify token
Map<String, dynamic> verify(String token, String guard, String expectedType) {
String secretKey = env('JWT_SECRET_KEY') ?? env<String>('APP_KEY');
try {
final jwt = JWT.verify(token, SecretKey('${env('APP_KEY')}$guard'));
final jwt = JWT.verify(
token,
SecretKey('$secretKey$guard'),
audience: env('JWT_AUDIENCE') == null
? null
: Audience.one(env<String>('JWT_AUDIENCE')),
jwtId: env<String?>('JWT_ID'),
issuer: env<String?>('JWT_ISSUER'),
subject: env<String?>('JWT_SUBJECT'),
);

if (jwt.payload['type'] != expectedType) {
throw Unauthenticated(message: 'Invalid token');
Expand Down

0 comments on commit d59b118

Please sign in to comment.