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';

<Steps>

<Step title="Get started">

This tutorial will show you how to integrate Logto into your PHP web application.

<ul>
  <li>The example uses Laravel, but the concepts are the same for other frameworks.</li>
  <li>This tutorial assumes your website is hosted on <code>{props.sampleUrls.origin}</code>.</li>
</ul>

```bash
composer require logto/sdk
```

</Step>

<Step title="Init LogtoClient">

Insert the following code into your PHP file:

<pre>
  <code className="language-php">
{`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}",
  ),
);`}
  </code>
</pre>

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(),
);
```

</Step>

<Step title="Implement the sign-in route">

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

<UriInputField name="redirectUris" />

<pre>
  <code className="language-php">
{`Route::get('/sign-in', function () {
  return redirect($client->signIn('${props.redirectUris[0] || props.sampleUrls.callback}'));
});`}
  </code>
</pre>

If you want to show the sign-up page on the first screen, you can set `interactionMode` to `signUp`:

<pre>
  <code className="language-php">
{`Route::get('/sign-in', function () {
  return redirect($client->signIn('${props.redirectUris[0] || props.sampleUrls.callback}', InteractionMode::signUp));
});`}
  </code>
</pre>

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.

</Step>

<Step title="Implement the callback route">

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
});
```

</Step>

<Step title="Implement the home page">

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 <a href='/sign-in'>Sign in</a>";
  }

  return (
    // Get local ID token claims
    $client->getIdTokenClaims()->modelDumpJson(excludeUnset: true)
    . "<br>"
    // Fetch user info from Logto userinfo endpoint
    $client->fetchUserInfo()->modelDumpJson(excludeUnset: true)
    . "<br><a href='/sign-out'>Sign out</a>"
  );
});
```

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).

</Step>

<Step title="Implement the sign-out route">

To clean up the Python session and Logto session, we can designate a post sign-out redierct URI. This is where Logto will redirect users after they sign out.

<UriInputField name="postLogoutRedirectUris" />

And a sign-out route can be implemented as follows:

<pre>
  <code className="language-python">
{`Route::get('/sign-out', function () {
  return redirect(
    // Redirect the user to the home page after a successful sign-out
    $client->signOut('${props.postLogoutRedirectUris[0] || props.sampleUrls.origin}')
  );
});`}
  </code>
</pre>

The post sign-out redierct URI 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).

</Step>

<Step title="Checkpoint: Test your application">

Now, you can test your application:

<ol>
  <li>Visit <code>{props.sampleUrls.origin}</code>, and you should see a "Not authenticated" message with a "Sign in" button.</li>
  <li>Click the "Sign in" button, and you should be redirected to the Logto sign-in page.</li>
  <li>After you sign in, you should be redirected back to <code>{props.sampleUrls.origin}</code>, and you should see your user info and a "Sign out" button.</li>
  <li>Click the "Sign out" button, and you should be redirected back to <code>{props.sampleUrls.origin}</code>, and you should see a "Not authenticated" message with a "Sign in" button.</li>
</ol>

</Step>

<Step title="Protect your routes">

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);
```

</Step>

</Steps>
