aboutsummaryrefslogtreecommitdiffhomepage
path: root/routes/auth.php
blob: 2127a460b8d28d69afab7d06d6bd6bb06a5cf4e4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
<?php

use Digitigrade\GlobalConfig;
use Digitigrade\HttpResponseStatus\BadRequest;
use Digitigrade\HttpResponseStatus\Forbidden;
use Digitigrade\Job\ExpireInboundAuthToken;
use Digitigrade\Job\RequestOutboundAuthToken;
use Digitigrade\Model\Instance;
use Digitigrade\Router;

Router::getInstance()->mount('/auth/main', function (array $args) {
    if (!isset($_GET['phase'])) {
        throw new BadRequest('you need to specify the `phase` GET parameter');
    }
    switch ($_GET['phase']) {

        case 'dialback':
            // send a secret to the dialback address!
            if (!isset($_GET['target'])) {
                throw new BadRequest('you need to specify the `target` GET parameter');
            }
            $domain = explode('/', $_GET['target'])[2];
            $instance = Instance::findByDomain($domain);
            if ($instance == null) {
                throw new RuntimeException("i can't find your instance information, sorry");
            }

            $instance->auth->secret = random_printable_bytes();
            $instance->auth->save();
            // TODO: schedule a job to throw away the secret after some time if it's not used?

            $data = http_build_query([
                'origin' => GlobalConfig::getInstance()->getCanonicalHost(),
                'secret' => $instance->auth->secret
            ]);
            $context = stream_context_create(['http' => [
                'method' => 'POST',
                'header' => 'Content-Type: application/x-www-form-urlencoded',
                'content' => $data
            ]]);
            // TODO: (maybe?) put this in a job instead of doing it right away?
            file_get_contents($_GET['target'], context: $context);

            http_response_code(202); // "Accepted"
            break;

        case 'token':
            // verify the secret, if it's good, issue a token
            if (!isset($_GET['secret'])) {
                throw new BadRequest('you need to specify the `secret` GET parameter');
            }
            $instance = Instance::findByDialbackSecret($_GET['secret']);
            if ($instance == null) {
                throw new Forbidden('the provided secret is invalid!');
            }

            $instance->auth->secret = null; // it's been used now
            $instance->auth->inboundToken = random_printable_bytes();
            $instance->auth->save();

            $expiry = new \DateTimeImmutable('1 day'); // this seems like a reasonable length of time?
            (new ExpireInboundAuthToken(
                $instance->domain,
                $instance->auth->inboundToken,
                $expiry
            ))->submit();

            json_response([
                'token' => $instance->auth->inboundToken,
                'expires' => $expiry->format('c')
            ]);

            break;

        case 'refresh':
            // verify the token, if it's good, replace it with a new one
            if (!isset($_GET['token'])) {
                throw new BadRequest('you need to specify the `token` GET parameter');
            }
            $instance = Instance::findByInboundAuthToken($_GET['token']);
            if ($instance == null) {
                throw new Forbidden('the provided token is invalid!');
            }

            $instance->auth->inboundToken = random_printable_bytes();
            $instance->auth->save();

            $expiry = new \DateTimeImmutable('1 day');
            (new ExpireInboundAuthToken(
                $instance->domain,
                $instance->auth->inboundToken,
                $expiry
            ))->submit();

            json_response([
                'token' => $instance->auth->inboundToken,
                'expires' => $expiry->format('c')
            ]);

            break;

        default:
            throw new BadRequest('invalid phase');
    }
});

Router::getInstance()->mount('/auth/dialback', function (array $args) {
    // just respond to the dialback challenge!
    if (!isset($_POST['origin'], $_POST['secret'])) {
        throw new BadRequest('send the `origin` and `secret` parameters as POST data please :3');
    }
    $instance = Instance::findByDomain($_POST['origin']);
    if ($instance == null) {
        throw new BadRequest("i can't seem to find your instance information!");
    }
    if (!isset($instance->endpoints->auth)) {
        throw new BadRequest("you don't seem to have an auth endpoint");
    }

    (new RequestOutboundAuthToken($instance->domain, $_POST['secret']))->submit();
    http_response_code(202); // "Accepted"
});