import * as client from 'openid-client'

// Prerequisites

let getCurrentUrl!: (...args: any) => URL
let server!: URL // Authorization server's Issuer Identifier URL
let clientId!: string
let clientSecret!: string
/**
 * Value used in the authorization request as redirect_uri pre-registered at the
 * Authorization Server.
 */
let redirect_uri!: string

// End of prerequisites

let config = await client.discovery(server, clientId, clientSecret)

let code_challenge_method = 'S256'
/**
 * The following (code_verifier and potentially nonce) MUST be generated for
 * every redirect to the authorization_endpoint. You must store the
 * code_verifier and nonce in the end-user session such that it can be recovered
 * as the user gets redirected from the authorization server back to your
 * application.
 */
let code_verifier = client.randomPKCECodeVerifier()
let code_challenge = await client.calculatePKCECodeChallenge(code_verifier)
let nonce!: string

{
  // redirect user to as.authorization_endpoint
  let parameters: Record<string, string> = {
    redirect_uri,
    scope: 'openid email',
    code_challenge,
    code_challenge_method,
  }

  /**
   * We cannot be sure the AS supports PKCE so we're going to use nonce too. Use
   * of PKCE is backwards compatible even if the AS doesn't support it which is
   * why we're using it regardless.
   */
  if (!config.serverMetadata().supportsPKCE()) {
    nonce = client.randomNonce()
    parameters.nonce = nonce
  }

  let redirectTo = client.buildAuthorizationUrl(config, parameters)

  console.log('redirecting to', redirectTo.href)
  // now redirect the user to redirectTo.href
}

// one eternity later, the user lands back on the redirect_uri
// Authorization Code Grant
let sub: string
let access_token: string
{
  let currentUrl: URL = getCurrentUrl()
  let tokens = await client.authorizationCodeGrant(config, currentUrl, {
    pkceCodeVerifier: code_verifier,
    expectedNonce: nonce,
    idTokenExpected: true,
  })

  console.log('Token Endpoint Response', tokens)
  ;({ access_token } = tokens)
  let claims = tokens.claims()!
  console.log('ID Token Claims', claims)
  ;({ sub } = claims)
}

// UserInfo Request
{
  let userInfo = await client.fetchUserInfo(config, access_token, sub)

  console.log('UserInfo Response', userInfo)
}
