zitadel

Форк
0
/
authrequest.jsx 
671 строка · 26.5 Кб
1
import React, { Fragment, useContext, useEffect, useState } from "react";
2
import { AuthRequestContext } from "../utils/authrequest";
3
import { Listbox } from "@headlessui/react";
4
import { Transition } from "@headlessui/react";
5
import { ChevronUpDownIcon, CheckIcon } from "@heroicons/react/24/solid";
6
import clsx from "clsx";
7
import { Buffer } from "buffer";
8
import { CopyToClipboard } from "react-copy-to-clipboard";
9
import BrowserOnly from "@docusaurus/BrowserOnly";
10

11
const LinkButton = ({
12
  instance,
13
  clientId,
14
  redirectUri,
15
  responseType,
16
  prompt,
17
  organizationId,
18
  authMethod,
19
  codeVerifier,
20
  scope,
21
  loginHint,
22
  idTokenHint,
23
}) => {
24
  const [copied, setCopied] = useState(false);
25

26
  return (
27
    <CopyToClipboard
28
      text={`https://zitadel.com/docs/apis/openidoauth/authrequest?instance=${encodeURIComponent(
29
        instance
30
      )}&client_id=${encodeURIComponent(
31
        clientId
32
      )}&redirect_uri=${encodeURIComponent(
33
        redirectUri
34
      )}&response_type=${encodeURIComponent(
35
        responseType
36
      )}&scope=${encodeURIComponent(scope)}&prompt=${encodeURIComponent(
37
        prompt
38
      )}&auth_method=${encodeURIComponent(
39
        authMethod
40
      )}&code_verifier=${encodeURIComponent(
41
        codeVerifier
42
      )}&login_hint=${encodeURIComponent(
43
        loginHint
44
      )}&id_token_hint=${encodeURIComponent(
45
        idTokenHint
46
      )}&organization_id=${encodeURIComponent(organizationId)}
47
  `}
48
      onCopy={() => {
49
        setCopied(true);
50
        setTimeout(() => {
51
          setCopied(false);
52
        }, 2000);
53
      }}
54
    >
55
      <button className="cursor-pointer border-none h-10 flex flex-row items-center py-2 px-4 text-white bg-gray-500 dark:bg-gray-600 hover:dark:bg-gray-500 hover:text-white rounded-md hover:no-underline font-semibold text-sm plausible-event-name=OIDC+Playground plausible-event-method=Save">
56
        Copy link
57
        {copied ? (
58
          <i className="text-[20px] ml-2 las la-clipboard-check"></i>
59
        ) : (
60
          <i className="text-[20px] ml-2 las la-clipboard"></i>
61
        )}
62
      </button>
63
    </CopyToClipboard>
64
  );
65
};
66

67
export function SetAuthRequest() {
68
  const {
69
    instance: [instance, setInstance],
70
    clientId: [clientId, setClientId],
71
    redirectUri: [redirectUri, setRedirectUri],
72
    responseType: [responseType, setResponseType],
73
    scope: [scope, setScope],
74
    prompt: [prompt, setPrompt],
75
    authMethod: [authMethod, setAuthMethod],
76
    codeVerifier: [codeVerifier, setCodeVerifier],
77
    codeChallenge: [codeChallenge, setCodeChallenge],
78
    loginHint: [loginHint, setLoginHint],
79
    idTokenHint: [idTokenHint, setIdTokenHint],
80
    organizationId: [organizationId, setOrganizationId],
81
  } = useContext(AuthRequestContext);
82

83
  const inputClasses = (error) =>
84
    clsx({
85
      "w-full sm:text-sm h-10 mb-2px rounded-md p-2 bg-input-light-background dark:bg-input-dark-background transition-colors duration-300": true,
86
      "border border-solid border-input-light-border dark:border-input-dark-border hover:border-black hover:dark:border-white focus:border-primary-light-500 focus:dark:border-primary-dark-500": true,
87
      "focus:outline-none focus:ring-0 text-base text-black dark:text-white placeholder:italic placeholder-gray-700 dark:placeholder-gray-700": true,
88
      "border border-warn-light-500 dark:border-warn-dark-500 hover:border-warn-light-500 hover:dark:border-warn-dark-500 focus:border-warn-light-500 focus:dark:border-warn-dark-500":
89
        error,
90
    });
91

92
  const labelClasses = "text-sm";
93
  const hintClasses = "mt-1 text-xs text-black/50 dark:text-white/50";
94

95
  const allResponseTypes = ["code", "id_token", "id_token token"];
96

97
  const allPrompts = ["", "login", "select_account", "create", "none"];
98

99
  const allAuthMethods = ["(none) PKCE", "Client Secret Basic"];
100

101
  const CodeSnipped = ({ cname, children }) => {
102
    return <span className={cname}>{children}</span>;
103
  };
104

105
  const allScopes = [
106
    "openid",
107
    "email",
108
    "profile",
109
    "address",
110
    "offline_access",
111
    "urn:zitadel:iam:org:project:id:zitadel:aud",
112
    "urn:zitadel:iam:user:metadata",
113
    `urn:zitadel:iam:org:id:${
114
      organizationId ? organizationId : "[organizationId]"
115
    }`,
116
  ];
117

118
  const [scopeState, setScopeState] = useState(
119
    [true, true, true, false, false, false, false, false]
120
    // new Array(allScopes.length).fill(false)
121
  );
122

123
  function toggleScope(position, forceChecked = false) {
124
    const updatedCheckedState = scopeState.map((item, index) =>
125
      index === position ? !item : item
126
    );
127

128
    if (forceChecked) {
129
      updatedCheckedState[position] = true;
130
    }
131

132
    setScopeState(updatedCheckedState);
133

134
    setScope(
135
      updatedCheckedState
136
        .map((checked, i) => (checked ? allScopes[i] : ""))
137
        .filter((s) => !!s)
138
        .join(" ")
139
    );
140
  }
141

142
  // Encoding functions for code_challenge
143

144
  async function string_to_sha256(message) {
145
    // encode as UTF-8
146
    const msgBuffer = new TextEncoder().encode(message);
147
    // hash the message
148
    const hashBuffer = await crypto.subtle.digest("SHA-256", msgBuffer);
149
    // return ArrayBuffer
150
    return hashBuffer;
151
  }
152
  async function encodeCodeChallenge(codeChallenge) {
153
    let arrayBuffer = await string_to_sha256(codeChallenge);
154
    let buffer = Buffer.from(arrayBuffer);
155
    let base64 = buffer.toString("base64");
156
    let base54url = base64_to_base64url(base64);
157
    return base54url;
158
  }
159
  var base64_to_base64url = function (input) {
160
    input = input.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
161
    return input;
162
  };
163

164
  useEffect(async () => {
165
    setCodeChallenge(await encodeCodeChallenge(codeVerifier));
166
  }, [codeVerifier]);
167

168
  useEffect(() => {
169
    const newScopeState = allScopes.map((s) => scope.includes(s));
170
    if (scopeState !== newScopeState) {
171
      setScopeState(newScopeState);
172
    }
173
  }, [scope]);
174

175
  return (
176
    <div className="bg-white/5 rounded-md p-6 shadow">
177
      <div className="flex flex-row justify-between">
178
        <h5 className="text-lg mt-0 mb-4 font-semibold">Your Domain</h5>
179
        <BrowserOnly>
180
          {() => (
181
            <LinkButton
182
              instance={instance}
183
              clientId={clientId}
184
              redirectUri={redirectUri}
185
              responseType={responseType}
186
              prompt={prompt}
187
              scope={scope}
188
              organizationId={organizationId}
189
              authMethod={authMethod}
190
              codeVerifier={codeVerifier}
191
              loginHint={loginHint}
192
              idTokenHint={idTokenHint}
193
            />
194
          )}
195
        </BrowserOnly>
196
      </div>
197
      <div className="flex flex-col">
198
        <label className={`${labelClasses} text-yellow-500`}>
199
          Instance Domain
200
        </label>
201
        <input
202
          className={inputClasses(false)}
203
          id="instance"
204
          value={instance}
205
          onChange={(event) => {
206
            const value = event.target.value;
207
            setInstance(value);
208
          }}
209
        />
210
        <span className={hintClasses}>
211
          The domain of your zitadel instance.
212
        </span>
213
      </div>
214

215
      <h5 className="text-lg mt-6 mb-2 font-semibold">Required Parameters</h5>
216

217
      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
218
        <div className="flex flex-col">
219
          <label className={`${labelClasses} text-green-500`}>Client ID</label>
220
          <input
221
            className={inputClasses(false)}
222
            id="client_id"
223
            value={clientId}
224
            onChange={(event) => {
225
              const value = event.target.value;
226
              setClientId(value);
227
            }}
228
          />
229
          <span className={hintClasses}>
230
            This is the resource id of an application. It's the application
231
            where you want your users to login.
232
          </span>
233
        </div>
234

235
        <div className="flex flex-col">
236
          <label className={`${labelClasses} text-blue-500`}>
237
            Redirect URI
238
          </label>
239
          <input
240
            className={inputClasses(false)}
241
            id="redirect_uri"
242
            value={redirectUri}
243
            onChange={(event) => {
244
              const value = event.target.value;
245
              setRedirectUri(value);
246
            }}
247
          />
248
          <span className={hintClasses}>
249
            Must be one of the pre-configured redirect uris for your
250
            application.
251
          </span>
252
        </div>
253

254
        <div className="flex flex-col">
255
          <label className={`${labelClasses} text-orange-500`}>
256
            ResponseType
257
          </label>
258
          <Listbox value={responseType} onChange={setResponseType}>
259
            <div className="relative">
260
              <Listbox.Button className="transition-colors duration-300 text-black dark:text-white h-10 relative w-full cursor-default rounded-md bg-white dark:bg-input-dark-background py-2 pl-3 pr-10 text-left focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 sm:text-sm border border-solid border-input-light-border dark:border-input-dark-border hover:border-black hover:dark:border-white focus:border-primary-light-500 focus:dark:border-primary-dark-500">
261
                <span className="block truncate">{responseType}</span>
262
                <span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
263
                  <ChevronUpDownIcon
264
                    className="h-5 w-5 text-gray-400"
265
                    aria-hidden="true"
266
                  />
267
                </span>
268
              </Listbox.Button>
269
              <span className={`${hintClasses} flex`}>
270
                Determines whether a code, id_token token or just id_token will
271
                be returned. Most use cases will need code.
272
              </span>
273
              <Transition
274
                as={Fragment}
275
                leave="transition ease-in duration-100"
276
                leaveFrom="opacity-100"
277
                leaveTo="opacity-0"
278
              >
279
                <Listbox.Options className="pl-0 list-none z-10 top-10 absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white dark:bg-background-dark-300 text-black dark:text-white py-1 text-base ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
280
                  {allResponseTypes.map((type, typeIdx) => (
281
                    <Listbox.Option
282
                      key={typeIdx}
283
                      className={({ active }) =>
284
                        `relative cursor-default select-none py-2 pl-10 pr-4 ${
285
                          active ? "bg-black/20 dark:bg-white/20" : ""
286
                        }`
287
                      }
288
                      value={type}
289
                    >
290
                      {({ selected }) => (
291
                        <>
292
                          <span
293
                            className={`block truncate ${
294
                              selected ? "font-medium" : "font-normal"
295
                            }`}
296
                          >
297
                            {type}
298
                          </span>
299
                          {selected ? (
300
                            <span className="absolute inset-y-0 left-0 flex items-center pl-3 text-orange-500 dark:text-orange-400">
301
                              <CheckIcon
302
                                className="h-5 w-5"
303
                                aria-hidden="true"
304
                              />
305
                            </span>
306
                          ) : null}
307
                        </>
308
                      )}
309
                    </Listbox.Option>
310
                  ))}
311
                </Listbox.Options>
312
              </Transition>
313
            </div>
314
          </Listbox>
315
        </div>
316
      </div>
317

318
      <div className="grid grid-cols-2 md:grid-cols-2 lg:grid-cols-3 gap-4 mt-6">
319
        <div className="flex flex-col">
320
          <label className={`${labelClasses} text-teal-600`}>
321
            Authentication method
322
          </label>
323
          <Listbox value={authMethod} onChange={setAuthMethod}>
324
            <div className="relative">
325
              <Listbox.Button className="transition-colors duration-300 text-black dark:text-white h-10 relative w-full cursor-default rounded-md bg-white dark:bg-input-dark-background py-2 pl-3 pr-10 text-left focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 sm:text-sm border border-solid border-input-light-border dark:border-input-dark-border hover:border-black hover:dark:border-white focus:border-primary-light-500 focus:dark:border-primary-dark-500">
326
                <span className="block truncate">{authMethod}</span>
327
                <span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
328
                  <ChevronUpDownIcon
329
                    className="h-5 w-5 text-gray-400"
330
                    aria-hidden="true"
331
                  />
332
                </span>
333
              </Listbox.Button>
334
              <span className={`${hintClasses} flex`}>
335
                Authentication method
336
              </span>
337
              <Transition
338
                as={Fragment}
339
                leave="transition ease-in duration-100"
340
                leaveFrom="opacity-100"
341
                leaveTo="opacity-0"
342
              >
343
                <Listbox.Options className="pl-0 list-none z-10 absolute top-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white dark:bg-background-dark-300 text-black dark:text-white py-1 text-base ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
344
                  {allAuthMethods.map((type, typeIdx) => (
345
                    <Listbox.Option
346
                      key={typeIdx}
347
                      className={({ active }) =>
348
                        `h-10 relative cursor-default select-none py-2 pl-10 pr-4 ${
349
                          active ? "bg-black/20 dark:bg-white/20" : ""
350
                        }`
351
                      }
352
                      value={type}
353
                    >
354
                      {({ selected }) => (
355
                        <>
356
                          <span
357
                            className={`block truncate ${
358
                              selected ? "font-medium" : "font-normal"
359
                            }`}
360
                          >
361
                            {type}
362
                          </span>
363
                          {selected ? (
364
                            <span className="absolute inset-y-0 left-0 flex items-center pl-3 text-cyan-500 dark:text-cyan-400">
365
                              <CheckIcon
366
                                className="h-5 w-5"
367
                                aria-hidden="true"
368
                              />
369
                            </span>
370
                          ) : null}
371
                        </>
372
                      )}
373
                    </Listbox.Option>
374
                  ))}
375
                </Listbox.Options>
376
              </Transition>
377
            </div>
378
          </Listbox>
379
        </div>
380
        {authMethod === "(none) PKCE" && (
381
          <div className="flex flex-col">
382
            <label className={`${labelClasses} text-teal-600`}>
383
              Code Verifier
384
            </label>
385
            <input
386
              className={inputClasses(false)}
387
              id="code_verifier"
388
              value={codeVerifier}
389
              onChange={(event) => {
390
                const value = event.target.value;
391
                setCodeVerifier(value);
392
              }}
393
            />
394
            <span className={hintClasses}>
395
              <span className="text-teal-600">Authentication method</span> PKCE
396
              requires a random string used to generate a{" "}
397
              <code>code_challenge</code>
398
            </span>
399
          </div>
400
        )}
401
      </div>
402

403
      <h5 className="text-lg mt-6 mb-2 font-semibold">Additional Parameters</h5>
404

405
      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
406
        <div>
407
          <div className="flex flex-col">
408
            <label className={`${labelClasses} text-cyan-500`}>Prompt</label>
409
            <Listbox value={prompt} onChange={setPrompt}>
410
              <div className="relative">
411
                <Listbox.Button className="transition-colors duration-300 text-black dark:text-white h-10 relative w-full cursor-default rounded-md bg-white dark:bg-input-dark-background py-2 pl-3 pr-10 text-left focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 sm:text-sm border border-solid border-input-light-border dark:border-input-dark-border hover:border-black hover:dark:border-white focus:border-primary-light-500 focus:dark:border-primary-dark-500">
412
                  <span className="block truncate">{prompt}</span>
413
                  <span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
414
                    <ChevronUpDownIcon
415
                      className="h-5 w-5 text-gray-400"
416
                      aria-hidden="true"
417
                    />
418
                  </span>
419
                </Listbox.Button>
420
                <span className={`${hintClasses} flex`}>
421
                  Define how the user should be prompted on login and register.
422
                </span>
423
                <Transition
424
                  as={Fragment}
425
                  leave="transition ease-in duration-100"
426
                  leaveFrom="opacity-100"
427
                  leaveTo="opacity-0"
428
                >
429
                  <Listbox.Options className="pl-0 list-none z-10 absolute top-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white dark:bg-background-dark-300 text-black dark:text-white py-1 text-base ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
430
                    {allPrompts.map((type, typeIdx) => (
431
                      <Listbox.Option
432
                        key={typeIdx}
433
                        className={({ active }) =>
434
                          `h-10 relative cursor-default select-none py-2 pl-10 pr-4 ${
435
                            active ? "bg-black/20 dark:bg-white/20" : ""
436
                          }`
437
                        }
438
                        value={type}
439
                      >
440
                        {({ selected }) => (
441
                          <>
442
                            <span
443
                              className={`block truncate ${
444
                                selected ? "font-medium" : "font-normal"
445
                              }`}
446
                            >
447
                              {type}
448
                            </span>
449
                            {selected ? (
450
                              <span className="absolute inset-y-0 left-0 flex items-center pl-3 text-cyan-500 dark:text-cyan-400">
451
                                <CheckIcon
452
                                  className="h-5 w-5"
453
                                  aria-hidden="true"
454
                                />
455
                              </span>
456
                            ) : null}
457
                          </>
458
                        )}
459
                      </Listbox.Option>
460
                    ))}
461
                  </Listbox.Options>
462
                </Transition>
463
              </div>
464
            </Listbox>
465
          </div>
466
        </div>
467

468
        {prompt === "select_account" && (
469
          <div className="flex flex-col">
470
            <label className={`${labelClasses} text-rose-500`}>
471
              Login hint
472
            </label>
473
            <input
474
              className={inputClasses(false)}
475
              id="login_hint"
476
              value={loginHint}
477
              onChange={(event) => {
478
                const value = event.target.value;
479
                setLoginHint(value);
480
              }}
481
            />
482
            <span className={hintClasses}>
483
              This in combination with a{" "}
484
              <span className="text-black dark:text-white">select_account</span>{" "}
485
              <span className="text-cyan-500">prompt</span> the login will
486
              preselect a user.
487
            </span>
488
          </div>
489
        )}
490

491
        {/* <div className="flex flex-col">
492
          <label className={`${labelClasses} text-blue-500`}>
493
            ID Token hint
494
          </label>
495
          <input
496
            className={inputClasses(false)}
497
            id="id_token_hint"
498
            value={idTokenHint}
499
            onChange={(event) => {
500
              const value = event.target.value;
501
              setIdTokenHint(value);
502
            }}
503
          />
504
          <span className={hintClasses}>
505
            This in combination with a{" "}
506
            <span className="text-black dark:text-white">select_account</span>{" "}
507
            <span className="text-emerald-500">prompt</span> the login will
508
            preselect a user.
509
          </span>
510
        </div> */}
511
      </div>
512

513
      <h5 className="text-lg mt-6 mb-2 font-semibold">Scopes</h5>
514

515
      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 gap-4">
516
        <div className="flex flex-col">
517
          <label className={`${labelClasses} text-purple-500`}>
518
            Organization ID
519
          </label>
520
          <input
521
            className={inputClasses(false)}
522
            id="organization_id"
523
            value={organizationId}
524
            onChange={(event) => {
525
              const value = event.target.value;
526
              setOrganizationId(value);
527
              allScopes[7] = `urn:zitadel:iam:org:id:${
528
                value ? value : "[organizationId]"
529
              }`;
530
              toggleScope(8, true);
531
              setScope(
532
                scopeState
533
                  .map((checked, i) => (checked ? allScopes[i] : ""))
534
                  .filter((s) => !!s)
535
                  .join(" ")
536
              );
537
            }}
538
          />
539
          <span className={hintClasses}>
540
            Enforce organization policies and user membership by requesting the{" "}
541
            <span className="text-purple-500">scope</span>{" "}
542
            <code>urn:zitadel:iam:org:id:{organizationId}</code>
543
          </span>
544
        </div>
545
      </div>
546
      <div className="py-4">
547
        <p className="text-sm mt-0 mb-0 text-purple-500">Scopes</p>
548
        <span className={`${hintClasses} flex mb-2`}>
549
          Request additional information about the user with scopes. The claims
550
          will be returned on the userinfo_endpoint or in the token (when
551
          configured).
552
        </span>
553
        {allScopes.map((scope, scopeIndex) => {
554
          return (
555
            <div key={`scope-${scope}`} className="flex flex-row items-center">
556
              <input
557
                type="checkbox"
558
                id={`scope_${scope}`}
559
                name="scopes"
560
                value={`${scope}`}
561
                checked={scopeState[scopeIndex]}
562
                onChange={() => {
563
                  toggleScope(scopeIndex);
564
                }}
565
              />
566
              <label className="ml-4" htmlFor={`scope_${scope}`}>
567
                {scope}{" "}
568
                {scopeIndex === 8 && scopeState[8] && !organizationId ? (
569
                  <strong className="text-red-500">
570
                    Organization ID missing!
571
                  </strong>
572
                ) : null}
573
              </label>
574
            </div>
575
          );
576
        })}
577
      </div>
578

579
      {/* <h5>Optional Parameters</h5>
580

581
      <div className={styles.grid}>
582
        <div className={styles.inputwrapper}>
583
          <label className={styles.label}>Id Token Hint</label>
584
          <input
585
            className={styles.input}
586
            id="id_token_hint"
587
            value={idTokenHint}
588
            onChange={(event) => {
589
              const value = event.target.value;
590
              setIdTokenHint(value);
591
            }}
592
          />
593
        </div>
594
      </div> */}
595

596
      <h5 className="text-lg mt-6 mb-2 font-semibold">
597
        Your authorization request
598
      </h5>
599

600
      <div className="rounded-md bg-gray-700 shadow dark:bg-black/10 p-2 flex flex-col items-center">
601
        <code className="text-sm w-full mb-4 bg-transparent border-none">
602
          <span className="text-yellow-500">
603
            {instance.endsWith("/") ? instance : instance + "/"}
604
          </span>
605
          <span className="text-white">oauth/v2/authorize?</span>
606
          <CodeSnipped cname="text-green-500">{`client_id=${encodeURIComponent(
607
            clientId
608
          )}`}</CodeSnipped>
609
          <CodeSnipped cname="text-blue-500">{`&redirect_uri=${encodeURIComponent(
610
            redirectUri
611
          )}`}</CodeSnipped>
612
          <CodeSnipped cname="text-orange-500">
613
            {`&response_type=${encodeURIComponent(responseType)}`}
614
          </CodeSnipped>
615
          <CodeSnipped cname="text-purple-500">{`&scope=${encodeURIComponent(
616
            scope
617
          )}`}</CodeSnipped>
618
          {prompt && (
619
            <CodeSnipped cname="text-cyan-500">{`&prompt=${encodeURIComponent(
620
              prompt
621
            )}`}</CodeSnipped>
622
          )}
623
          {loginHint && prompt === "select_account" && (
624
            <CodeSnipped cname="text-rose-500">{`&login_hint=${encodeURIComponent(
625
              loginHint
626
            )}`}</CodeSnipped>
627
          )}
628
          {authMethod === "(none) PKCE" && (
629
            <CodeSnipped cname="text-teal-600">{`&code_challenge=${encodeURIComponent(
630
              codeChallenge
631
            )}&code_challenge_method=S256`}</CodeSnipped>
632
          )}
633
        </code>
634

635
        <a
636
          onClick={() => {
637
            window.plausible("OIDC Playground", {
638
              props: { method: "Try it out", pageloc: "Authorize" },
639
            });
640
          }}
641
          target="_blank"
642
          className="mt-2 flex flex-row items-center py-2 px-4 text-white bg-green-500 dark:bg-green-600 hover:dark:bg-green-500 hover:text-white rounded-md hover:no-underline font-semibold text-sm plausible-event-name=OIDC+Playground plausible-event-method=Try+it+out"
643
          href={`${
644
            instance.endsWith("/") ? instance : instance + "/"
645
          }oauth/v2/authorize?client_id=${encodeURIComponent(
646
            clientId
647
          )}&redirect_uri=${encodeURIComponent(
648
            redirectUri
649
          )}&response_type=${encodeURIComponent(
650
            responseType
651
          )}&scope=${encodeURIComponent(scope)}${
652
            prompt ? `&prompt=${encodeURIComponent(prompt)}` : ""
653
          }${
654
            loginHint && prompt === "select_account"
655
              ? `&login_hint=${encodeURIComponent(loginHint)}`
656
              : ""
657
          }${
658
            authMethod === "(none) PKCE"
659
              ? `&code_challenge=${encodeURIComponent(
660
                  codeChallenge
661
                )}&code_challenge_method=S256`
662
              : ""
663
          }`}
664
        >
665
          <span>Try it out</span>
666
          <i className="text-white text-md ml-2 las la-external-link-alt"></i>
667
        </a>
668
      </div>
669
    </div>
670
  );
671
}
672

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.