From 0f98330455ceb3419c13a302c45f9cf1a5aaffe0 Mon Sep 17 00:00:00 2001 From: Gao Sun Date: Thu, 17 Aug 2023 11:40:08 +0800 Subject: [PATCH] feat(console): php guide (#4351) --- .../src/assets/docs/guides/web-php/README.mdx | 208 +++++++++++++++++- .../src/assets/docs/guides/web-php/index.ts | 2 +- .../assets/docs/guides/web-python/README.mdx | 6 +- 3 files changed, 211 insertions(+), 5 deletions(-) diff --git a/packages/console/src/assets/docs/guides/web-php/README.mdx b/packages/console/src/assets/docs/guides/web-php/README.mdx index ae759f749..3fe901458 100644 --- a/packages/console/src/assets/docs/guides/web-php/README.mdx +++ b/packages/console/src/assets/docs/guides/web-php/README.mdx @@ -1 +1,207 @@ -## Replace this with actual guide \ No newline at end of file +import UriInputField from '@/mdx-components-v2/UriInputField'; +import Tabs from '@mdx/components/Tabs'; +import TabItem from '@mdx/components/TabItem'; +import InlineNotification from '@/ds-components/InlineNotification'; +import { buildIdGenerator } from '@logto/shared/universal'; +import Steps from '@/mdx-components-v2/Steps'; +import Step from '@/mdx-components-v2/Step'; + + + + + +This tutorial will show you how to integrate Logto into your PHP web application. + +
    +
  • The example uses Laravel, but the concepts are the same for other frameworks.
  • +
  • This tutorial assumes your website is hosted on {props.sampleUrls.origin}.
  • +
+ +```bash +composer require logto/sdk +``` + +
+ + + +Insert the following code into your PHP file: + +
+  
+{`use logto\sdk\LogtoClient;
+use Logto\Sdk\LogtoConfig;
+
+$client = new LogtoClient(
+  new LogtoConfig(
+    endpoint: "${props.endpoint}",${props.alternativeEndpoint ? ' // or "${props.alternativeEndpoint}"' : ''}
+    appId: "${props.app.id}",
+    appSecret: "${props.app.secret}",
+  ),
+);`}
+  
+
+ +By default, the SDK uses the built-in PHP session to store the Logto data. If you want to use other storage, you can pass a custom storage object as the second parameter: + +```php +$client = new LogtoClient( + new LogtoConfig( + // ... + ), + new YourCustomStorage(), +); +``` + +
+ + + +

+First, let’s enter your redirect URI. E.g. {props.sampleUrls.callback}. This is where Logto will redirect users after they sign in. +

+ + + +
+  
+{`Route::get('/sign-in', function () {
+  return redirect($client->signIn('${props.redirectUris[0] || props.sampleUrls.callback}'));
+});`}
+  
+
+ +If you want to show the sign-up page on the first screen, you can set `interactionMode` to `signUp`: + +
+  
+{`Route::get('/sign-in', function () {
+  return redirect($client->signIn('${props.redirectUris[0] || props.sampleUrls.callback}', InteractionMode::signUp));
+});`}
+  
+
+ +Now, whenever your users visit `/sign-in`, it will start a new sign-in attempt and redirect the user to the Logto sign-in page. + +> **Note** +> Creating a sign-in route isn't the only way to start a sign-in attempt. You can always use the `signIn` method to get the sign-in URL and redirect the user to it. + +
+ + + +After the user signs in, Logto will redirect the user to the callback URL you set in the Logto Console. In this example, we use `/callback` as the callback URL: + +```php +Route::get('/callback', function () { + try { + $client->handleSignInCallback(); // Handle a lot of stuff + } catch (\Throwable $exception) { + return $exception; // Change this to your error handling logic + } + return redirect('/'); // Redirect the user to the home page after a successful sign-in +}); +``` + + + + + +Here we implement a simple home page for demonstration: + +- If the user is not signed in, show a sign-in button; +- If the user is signed in, show some basic information about the user. + +```php +Route::get('/', function () { + if ($client->isAuthenticated() === false) { + return "Not authenticated Sign in"; + } + + return ( + // Get local ID token claims + $client->getIdTokenClaims()->modelDumpJson(excludeUnset: true) + . "
" + // Fetch user info from Logto userinfo endpoint + $client->fetchUserInfo()->modelDumpJson(excludeUnset: true) + . "
Sign out" + ); +}); +``` + +Our data models are based on [JsonModel](https://github.com/logto-io/php/blob/HEAD/docs/api/classes/Logto/Sdk/Models/JsonModel.md), which is safe to accept undefined keys while encoding or decoding JSON. + +Note that a field (claim) with `null` value doesn't mean the field is set. The reason may be the related scope is not requested, or the user doesn't have the field. + +For example, if we didn't request the `email` scope when signing in, and the `email` field will be `null`. However, if we requested the `email` scope, the `email` field will be the user's email address if available. + +To learn more about scopes and claims, see [Scopes and claims](https://github.com/logto-io/php/blob/HEAD/docs/tutorial.md#scopes-and-claims). + +
+ + + +To clean up the Python session and Logto session, a sign-out route can be implemented as follows: + +
+  
+{`Route::get('/sign-out', function () {
+  return redirect(
+    // Redirect the user to the home page after a successful sign-out
+    $client->signOut('${props.sampleUrls.origin}')
+  );
+});`}
+  
+
+ +`postLogoutRedirectUri` is optional, and if not provided, the user will be redirected to a Logto default page after a successful sign-out (without redirecting back to your application). + +> The name `postLogoutRedirectUri` is from the [OpenID Connect RP-Initiated Logout](https://openid.net/specs/openid-connect-rpinitiated-1_0.html) specification. Although Logto uses "sign-out" instead of "logout", the concept is the same. + +
+ + + +Now, you can test your application: + +
    +
  1. Visit {props.sampleUrls.origin}, and you should see a "Not authenticated" message with a "Sign in" button.
  2. +
  3. Click the "Sign in" button, and you should be redirected to the Logto sign-in page.
  4. +
  5. After you sign in, you should be redirected back to {props.sampleUrls.origin}, and you should see your user info and a "Sign out" button.
  6. +
  7. Click the "Sign out" button, and you should be redirected back to {props.sampleUrls.origin}, and you should see a "Not authenticated" message with a "Sign in" button.
  8. +
+ +
+ + + +Now, you have a working sign-in flow, but your routes are still unprotected. Per the framework you use, you can create a middleware to protect your routes. For example, in Laravel, you can create a middleware as follows: + +```php +namespace App\Http\Middleware; + +use Closure; + +class LogtoAuth +{ + public function handle($request, Closure $next) + { + if ($client->isAuthenticated() === false) { + return redirect('/sign-in'); + } + return $next($request); + } +} +``` + +Then, you can apply this middleware to your routes: + +```php +Route::get('/protected', function () { + return "Protected page"; +})->middleware(LogtoAuth::class); +``` + + + +
diff --git a/packages/console/src/assets/docs/guides/web-php/index.ts b/packages/console/src/assets/docs/guides/web-php/index.ts index 424415be8..b2f8ce94d 100644 --- a/packages/console/src/assets/docs/guides/web-php/index.ts +++ b/packages/console/src/assets/docs/guides/web-php/index.ts @@ -4,7 +4,7 @@ import { type GuideMetadata } from '../types'; const metadata: Readonly = Object.freeze({ name: 'PHP', - description: 'PHP is the best language in the world.', + description: 'Integrate Logto into your PHP web app, such as Lavarel.', target: ApplicationType.Traditional, }); diff --git a/packages/console/src/assets/docs/guides/web-python/README.mdx b/packages/console/src/assets/docs/guides/web-python/README.mdx index 46dbb2dd0..93917faa2 100644 --- a/packages/console/src/assets/docs/guides/web-python/README.mdx +++ b/packages/console/src/assets/docs/guides/web-python/README.mdx @@ -26,7 +26,7 @@ pip install logto # or `poetry add logto` or whatever you use -Insert the following code into your python file: +Insert the following code into your Python file:
   
@@ -34,7 +34,7 @@ Insert the following code into your python file:
 
 client = LogtoClient(
     LogtoConfig(
-        endpoint= "${props.endpoint}",${props.alternativeEndpoint ? ' // or "${props.alternativeEndpoint}"' : ''}
+        endpoint="${props.endpoint}",${props.alternativeEndpoint ? ' # or "${props.alternativeEndpoint}"' : ''}
         appId="${props.app.id}",
         appSecret="${props.app.secret}",
     )
@@ -152,7 +152,7 @@ Adding `exclude_unset=True` will exclude unset fields from the JSON output, whic
 
 For example, if we didn't request the `email` scope when signing in, and the `email` field will be excluded from the JSON output. However, if we requested the `email` scope, but the user doesn't have an email address, the `email` field will be included in the JSON output with a `null` value.
 
-To learn more about scopes and claims, see [Scopes and claims](https://github.com/logto-io/python/blob/master/docs/tutorial.md#scopes-and-claims).
+To learn more about scopes and claims, see [Scopes and claims](https://github.com/logto-io/python/blob/HEAD/docs/tutorial.md#scopes-and-claims).