diff --git a/composer.json b/composer.json
index d957407..63dfb2b 100644
--- a/composer.json
+++ b/composer.json
@@ -4,7 +4,7 @@
     "mf2/mf2": "^0.4.3",
     "indieweb/mention-client": "~1.1",
     "indieweb/representative-h-card": "0.1.*",
-    "indieauth/client": "0.2.*",
+    "indieauth/client": "^1.0",
     "firebase/php-jwt": "~3.0",
     "league/route": "~1.2",
     "league/plates": "~3.1",
diff --git a/composer.lock b/composer.lock
index b4310a3..355be3c 100644
--- a/composer.lock
+++ b/composer.lock
@@ -1,51 +1,11 @@
 {
     "_readme": [
         "This file locks the dependencies of your project to a known state",
-        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "d36cff93d0bbe63f248f5c5ad541f2c9",
+    "content-hash": "2e3a9f1ddef93aa21e064fcee6c98176",
     "packages": [
-        {
-            "name": "barnabywalters/mf-cleaner",
-            "version": "v0.1.4",
-            "source": {
-                "type": "git",
-                "url": "https://github.com/barnabywalters/php-mf-cleaner.git",
-                "reference": "ef6a16628db6e8aee2b4f8bb8093d18c24b74cd4"
-            },
-            "dist": {
-                "type": "zip",
-                "url": "https://api.github.com/repos/barnabywalters/php-mf-cleaner/zipball/ef6a16628db6e8aee2b4f8bb8093d18c24b74cd4",
-                "reference": "ef6a16628db6e8aee2b4f8bb8093d18c24b74cd4",
-                "shasum": ""
-            },
-            "require-dev": {
-                "php": ">=5.3",
-                "phpunit/phpunit": "*"
-            },
-            "suggest": {
-                "mf2/mf2": "To parse microformats2 structures from (X)HTML"
-            },
-            "type": "library",
-            "autoload": {
-                "files": [
-                    "src/BarnabyWalters/Mf2/Functions.php"
-                ]
-            },
-            "notification-url": "https://packagist.org/downloads/",
-            "license": [
-                "MIT"
-            ],
-            "authors": [
-                {
-                    "name": "Barnaby Walters",
-                    "email": "barnaby@waterpigs.co.uk"
-                }
-            ],
-            "description": "Cleans up microformats2 array structures",
-            "time": "2014-10-06T23:11:15+00:00"
-        },
         {
             "name": "camspiers/json-pretty",
             "version": "1.0.2",
@@ -400,24 +360,27 @@
         },
         {
             "name": "indieauth/client",
-            "version": "0.2.2",
+            "version": "1.0.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/indieweb/indieauth-client-php.git",
-                "reference": "225ece31ddafaee3348eabdc915422c457498a84"
+                "reference": "d551eeb268100b87ebc7a05ae796a858550e700a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/indieweb/indieauth-client-php/zipball/225ece31ddafaee3348eabdc915422c457498a84",
-                "reference": "225ece31ddafaee3348eabdc915422c457498a84",
+                "url": "https://api.github.com/repos/indieweb/indieauth-client-php/zipball/d551eeb268100b87ebc7a05ae796a858550e700a",
+                "reference": "d551eeb268100b87ebc7a05ae796a858550e700a",
                 "shasum": ""
             },
             "require": {
-                "barnabywalters/mf-cleaner": "0.*",
-                "indieweb/link-rel-parser": "0.1.*",
-                "mf2/mf2": "~0.3",
+                "indieweb/representative-h-card": "^0.1.2",
+                "mf2/mf2": ">=0.3.2",
+                "p3k/http": ">=0.1.6",
                 "php": ">5.3.0"
             },
+            "require-dev": {
+                "phpunit/phpunit": "4.8.*"
+            },
             "type": "library",
             "autoload": {
                 "psr-0": {
@@ -426,16 +389,16 @@
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "Apache 2.0"
+                "Apache-2.0"
             ],
             "authors": [
                 {
                     "name": "Aaron Parecki",
-                    "homepage": "http://aaronparecki.com"
+                    "homepage": "https://aaronparecki.com"
                 }
             ],
             "description": "IndieAuth Client Library",
-            "time": "2017-07-01T15:43:45+00:00"
+            "time": "2020-11-26T19:41:45+00:00"
         },
         {
             "name": "indieweb/link-rel-parser",
@@ -1385,12 +1348,12 @@
             "version": "v1.0.3",
             "source": {
                 "type": "git",
-                "url": "https://github.com/nrk/predis.git",
+                "url": "https://github.com/predis/predis.git",
                 "reference": "84060b9034d756b4d79641667d7f9efe1aeb8e04"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/nrk/predis/zipball/84060b9034d756b4d79641667d7f9efe1aeb8e04",
+                "url": "https://api.github.com/repos/predis/predis/zipball/84060b9034d756b4d79641667d7f9efe1aeb8e04",
                 "reference": "84060b9034d756b4d79641667d7f9efe1aeb8e04",
                 "shasum": ""
             },
@@ -1740,10 +1703,51 @@
                 "xml",
                 "zf"
             ],
+            "abandoned": "laminas/laminas-xml",
             "time": "2018-04-30T15:11:04+00:00"
         }
     ],
     "packages-dev": [
+        {
+            "name": "barnabywalters/mf-cleaner",
+            "version": "v0.1.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/barnabywalters/php-mf-cleaner.git",
+                "reference": "ef6a16628db6e8aee2b4f8bb8093d18c24b74cd4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/barnabywalters/php-mf-cleaner/zipball/ef6a16628db6e8aee2b4f8bb8093d18c24b74cd4",
+                "reference": "ef6a16628db6e8aee2b4f8bb8093d18c24b74cd4",
+                "shasum": ""
+            },
+            "require-dev": {
+                "php": ">=5.3",
+                "phpunit/phpunit": "*"
+            },
+            "suggest": {
+                "mf2/mf2": "To parse microformats2 structures from (X)HTML"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/BarnabyWalters/Mf2/Functions.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Barnaby Walters",
+                    "email": "barnaby@waterpigs.co.uk"
+                }
+            ],
+            "description": "Cleans up microformats2 array structures",
+            "time": "2014-10-06T23:11:15+00:00"
+        },
         {
             "name": "doctrine/instantiator",
             "version": "1.0.5",
@@ -2190,6 +2194,7 @@
             "keywords": [
                 "tokenizer"
             ],
+            "abandoned": true,
             "time": "2015-09-15T10:49:45+00:00"
         },
         {
@@ -2321,6 +2326,7 @@
                 "mock",
                 "xunit"
             ],
+            "abandoned": true,
             "time": "2016-03-24T05:58:25+00:00"
         },
         {
@@ -2893,5 +2899,6 @@
     "platform": {
         "php": ">=5.5"
     },
-    "platform-dev": []
+    "platform-dev": [],
+    "plugin-api-version": "1.1.0"
 }
diff --git a/controllers/Auth.php b/controllers/Auth.php
index f520756..7d21257 100644
--- a/controllers/Auth.php
+++ b/controllers/Auth.php
@@ -41,14 +41,29 @@ class Auth {
       return $response;
     }
 
+    // Check if the user's URL defines an authorization endpoint
+    $authorizationEndpoint = IndieAuth\Client::discoverAuthorizationEndpoint($me);
+    if(!$authorizationEndpoint) {
+      $authorizationEndpoint = Config::$defaultAuthorizationEndpoint;
+    }
+
+    $codeVerifier = IndieAuth\Client::generatePKCECodeVerifier();
     $state = JWT::encode([
+      'az' => $authorizationEndpoint,
       'me' => $me,
+      'code_verifier' => $codeVerifier,
       'return_to' => $request->get('return_to'),
       'time' => time(),
       'exp' => time()+300 // verified by the JWT library
     ], Config::$secretKey);
 
-    $authorizationURL = IndieAuth\Client::buildAuthorizationURL(Config::$defaultAuthorizationEndpoint, $me, self::_buildRedirectURI(), Config::$clientID, $state);
+    $authorizationURL = IndieAuth\Client::buildAuthorizationURL($authorizationEndpoint, [
+      'me' => $me,
+      'redirect_uri' => self::_buildRedirectURI(),
+      'client_id' => Config::$clientID,
+      'state' => $state,
+      'code_verifier' => $codeVerifier,
+    ]);
 
     $response->setStatusCode(302);
     $response->headers->set('Location', $authorizationURL);
@@ -87,26 +102,46 @@ class Auth {
       return $response;
     }
 
-    $authorizationEndpoint = Config::$defaultAuthorizationEndpoint;
+    $authorizationEndpoint = $state->az;
 
     // Verify the code with the auth server
-    $token = IndieAuth\Client::verifyIndieAuthCode($authorizationEndpoint, $request->get('code'), $state->me, self::_buildRedirectURI(), Config::$clientID, true);
-
-    if(!array_key_exists('auth', $token) || !array_key_exists('me', $token['auth'])) {
-      // The auth server didn't return a "me" URL
+    $data = IndieAuth\Client::exchangeAuthorizationCode($state->az, [
+      'code' => $request->get('code'),
+      'redirect_uri' => self::_buildRedirectURI(),
+      'client_id' => Config::$clientID,
+      'code_verifier' => $state->code_verifier,
+    ]);
+
+    if(!isset($data['response']['me'])) {
+      // The authorization server didn't return a "me" URL
       $response->setContent(view('login', [
         'title' => 'Sign In to Telegraph',
         'error' => 'Invalid Auth Server Response',
-        'error_description' => 'The authorization server ('.$authorizationEndpoint.') did not return a valid response:
HTTP '.$token['response_code']."\n\n".htmlspecialchars($token['response']).'' + 'error_description' => 'The authorization server ('.$authorizationEndpoint.') did not return a valid response:
HTTP '.$data['response_code']."\n\n".htmlspecialchars(json_encode($data)).'' ])); return $response; } + // Verify the authorization endpoint matches + if($data['response']['me'] != $state->me) { + $newAuthorizationEndpoint = IndieAuth\Client::discoverAuthorizationEndpoint($data['response']['me']); + if($newAuthorizationEndpoint != $authorizationEndpoint) { + $response->setContent(view('login', [ + 'title' => 'Sign In to Telegraph', + 'error' => 'Invalid Authorization Endpoint', + 'error_description' => 'The authorization endpoint for the returned profile URL ('.$data['response']['me'].') did not match the authorization endpoint used to begin the login.' + ])); + return $response; + } + } + + $me = $data['response']['me']; + // Create or load the user - $user = ORM::for_table('users')->where('url', $token['auth']['me'])->find_one(); + $user = ORM::for_table('users')->where('url', $me)->find_one(); if(!$user) { $user = ORM::for_table('users')->create(); - $user->url = $token['auth']['me']; + $user->url = $me; $user->created_at = date('Y-m-d H:i:s'); $user->last_login = date('Y-m-d H:i:s'); $user->save(); @@ -114,7 +149,7 @@ class Auth { // Create a site for them with the default role $site = ORM::for_table('sites')->create(); $site->name = 'My Website'; - $site->url = $token['auth']['me']; + $site->url = $me; $site->created_by = $user->id; $site->created_at = date('Y-m-d H:i:s'); $site->save();