Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
62.96% covered (warning)
62.96%
17 / 27
33.33% covered (danger)
33.33%
1 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
AuthService
62.96% covered (warning)
62.96%
17 / 27
33.33% covered (danger)
33.33%
1 / 3
11.25
0.00% covered (danger)
0.00%
0 / 1
 login
92.86% covered (success)
92.86%
13 / 14
0.00% covered (danger)
0.00%
0 / 1
4.01
 generateCrossAppToken
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 validateCrossAppToken
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3namespace App\Services;
4
5use App\Models\TblAuthUsers;
6use Carbon\Carbon;
7use Illuminate\Support\Facades\Auth;
8use Illuminate\Support\Facades\Hash;
9use Illuminate\Support\Facades\Log;
10use Illuminate\Support\Str;
11
12class AuthService
13{
14    /**
15     * Authenticate a user and generate/update their API token.
16     *
17     * @param string $email User's email
18     * @param string $password User's password
19     * @param string|null $token Pre-generated token (optional)
20     * @return array<string, mixed> ['api_token' => string, 'token_expires_at' => Carbon]
21     * @throws \Exception If authentication fails (codes: 404, 401)
22     */
23    public function login(string $email, string $password, ?string $token): array
24    {
25        $user = TblAuthUsers::where('email', $email)->first();
26
27        if (!$user) {
28            throw new \Exception("User $email not found", 404);
29        }
30
31        if (!Hash::check($password, $user->password)) {
32            throw new \Exception("Invalid credentials for user: $email" , 401);
33        }
34
35        if(!$token){
36            $token = Str::random(60);
37        }
38
39        $tokenExpires = Carbon::now()->addHours(24);
40
41        $result = [
42            'api_token' => $token,
43            'token_expires_at' => $tokenExpires,
44        ];
45
46        $user->update($result);
47
48        return $result;
49    }
50
51    /**
52     * Generate a cross-application token with HMAC signature.
53     *
54     * @param string|null $rawToken Token to sign (generates one if null)
55     * @return string Format: "rawToken|hmacSignature"
56     */
57    public function generateCrossAppToken($token=null): string
58    {
59        $rawToken = $token?? Str::random(60);
60        $secret = env('SHARED_SECRET');
61
62        $signature = hash_hmac('sha256', $rawToken, $secret);
63
64        return $rawToken . '|' . $signature;
65    }
66
67    /**
68     * Validate a cross-application token.
69     *
70     * @param string $receivedToken Format: "rawToken|hmacSignature"
71     * @return string Returns the raw token if valid
72     * @throws \Exception If token is invalid (code: 401)
73     */
74    public function validateCrossAppToken(string $receivedToken): string
75    {
76        $parts = explode('|', $receivedToken);
77        if (count($parts) !== 2){
78            throw new \Exception("Invalid token", 401);
79        }
80
81        [$rawToken, $receivedSignature] = $parts;
82        $secret = env('SHARED_SECRET');
83
84        $expectedSignature = hash_hmac('sha256', $rawToken, $secret);
85
86        if(!hash_equals($expectedSignature, $receivedSignature)){
87            throw new \Exception("Invalid token", 401);
88        }
89
90        return $rawToken;
91    }
92}