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\Hash;
8use Illuminate\Support\Str;
9
10class AuthService
11{
12    /**
13     * Authenticate a user and generate/update their API token.
14     *
15     * @param  string  $email  User's email
16     * @param  string  $password  User's password
17     * @param  string|null  $token  Pre-generated token (optional)
18     * @return array<string, mixed> ['api_token' => string, 'token_expires_at' => Carbon]
19     *
20     * @throws \Exception If authentication fails (codes: 404, 401)
21     */
22    public function login(string $email, string $password, ?string $token): array
23    {
24        $user = TblAuthUsers::where('email', $email)->first();
25
26        if (! $user) {
27            throw new \Exception("User $email not found", 404);
28        }
29
30        if (! Hash::check($password, $user->password)) {
31            throw new \Exception("Invalid credentials for user: $email", 401);
32        }
33
34        if (! $token) {
35            $token = Str::random(60);
36        }
37
38        $tokenExpires = Carbon::now()->addHours(24);
39
40        $result = [
41            'api_token' => $token,
42            'token_expires_at' => $tokenExpires,
43        ];
44
45        $user->update($result);
46
47        return $result;
48    }
49
50    /**
51     * Generate a cross-application token with HMAC signature.
52     *
53     * @param  string|null  $token  Token to sign (generates one if null)
54     * @return string Format: "rawToken|hmacSignature"
55     */
56    public function generateCrossAppToken($token = null): string
57    {
58        $rawToken = $token ?? Str::random(60);
59        $secret = env('SHARED_SECRET');
60
61        $signature = hash_hmac('sha256', $rawToken, $secret);
62
63        return $rawToken.'|'.$signature;
64    }
65
66    /**
67     * Validate a cross-application token.
68     *
69     * @param  string  $receivedToken  Format: "rawToken|hmacSignature"
70     * @return string Returns the raw token if valid
71     *
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}