BearerFetcher::fromRequest()   B
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 24
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 24
rs 8.9713
cc 3
eloc 12
nc 3
nop 1
1
<?php
2
3
namespace Bgy\OAuth2ServerBundle\Security\Utils;
4
5
use Symfony\Component\HttpFoundation\Request;
6
7
/**
8
 * @author Boris Guéry <[email protected]>
9
 */
10
class BearerFetcher
11
{
12
    public static function fromRequest(Request $request)
13
    {
14
        $tokens = [];
15
        $tokens[] = self::fromRequestHeaders($request);
16
        $tokens[] = self::fromFormEncodedBody($request);
17
        $tokens[] = self::fromQuery($request);
18
19
        $tokens = array_filter($tokens, function($value) {
20
            return null !== $value;
21
        });
22
23
        if (count($tokens) > 1) {
24
25
            throw new \LogicException('More than one token were found.');
26
        }
27
28
        if (count($tokens) < 1) {
29
            // Don't throw exception here as we may want to allow non-authenticated
30
            // requests.
31
            return null;
32
        }
33
34
        return reset($tokens);
35
    }
36
37
    private function fromRequestHeaders(Request $request)
38
    {
39
        $header = null;
40
        if (!$request->headers->has('authorization')) {
41
            // The Authorization header may not be passed to PHP by Apache;
42
            // Trying to obtain it through apache_request_headers()
43
            if (function_exists('apache_request_headers')) {
44
                $headers = apache_request_headers();
45
                if (is_array($headers)) {
46
                    // Server-side fix for bug in old Android versions (a nice side-effect of this fix means we don't care about capitalization for Authorization)
47
                    $headers = array_combine(array_map('ucwords', array_keys($headers)), array_values($headers));
48
                    if (isset($headers['Authorization'])) {
49
                        $header = $headers['Authorization'];
50
                    }
51
                }
52
            }
53
        } else {
54
            $header = $request->headers->get('authorization');
55
        }
56
57
        if (!$header) {
58
            return null;
59
        }
60
61
        if (!preg_match('/' . preg_quote('Bearer', '/') . '\s(\S+)/', $header, $matches)) {
62
63
            return null;
64
        }
65
66
        $token = $matches[1];
67
68
        return $token;
69
    }
70
71
    private function fromFormEncodedBody(Request $request)
72
    {
73
        if (false === $request->headers->get('content-type')) {
74
75
            return null;
76
        }
77
        $contentType = $request->headers->get('content-type');
78
79
        if (!preg_match('/^application\/x-www-form-urlencoded([\s|;].*)?$/', $contentType)) {
80
81
            return null;
82
        }
83
84
        if ('GET' === $request->getMethod()) {
85
86
            return null;
87
        }
88
89
        // S2 request only decodes form encoded parameters for PUT, DELETE, PATCH. Because we are not so picky, we can't use Request::$request parameter bag...
90
        $body = $request->getContent();
91
        parse_str($body, $parameters);
92
93
        if (false === is_array($parameters)) {
94
95
            return null;
96
        }
97
98
        if (false === array_key_exists('access_token', $parameters)) {
99
100
            return null;
101
        }
102
103
        $token = $parameters['access_token'];
104
105
        return $token;
106
    }
107
108
    private function fromQuery(Request $request)
109
    {
110
        return $request->query->get('access_token', null);
111
    }
112
}
113