# Javascript Library - picket-js

### Installation

```shell
npm install --save "@picketapi/picket-js"
```

### Usage - Quick Start

The Picket constructor creates a new instance of the Picket class. It takes a *publishable API key* as a parameter.

```typescript
import Picket from "@picketapi/picket-js";
const picket = new Picket("YOUR_PUBLISHABLE_KEY_HERE");
```

We’ve placed a placeholder publishable API key in this example. Replace it with your [actual publishable API key](https://picketapi.com/dashboard).

```typescript
import Picket from "@picketapi/picket-js";
const picket = new Picket("YOUR_PUBLISHABLE_KEY_HERE");

const { accessToken, user } = await picket.login();
```

`login` optionally takes in *token ownership requirements* parameter. You can use this if you only want to allow users to login if they have a specific token, commonly referred to as [token gating](https://docs.picketapi.com/picket-docs/quick-start-guides/quick-start-guides/token-gating-ethereum-evm).

### Login

`login` is the easiest way to use Picket with your web, mobile, or native application. This enables you to securely login users via their wallet with a single line of code.&#x20;

#### Wallet Authentication

{% tabs %}
{% tab title="Ethereum (Mainnet)" %}

```typescript
// default chain is Ethereum
const { user } = await picket.login();
// or specifiy it explicitly
const { user } = await picket.login({ chain: "ethereum" });
```

{% endtab %}

{% tab title="Solana" %}

```typescript
const { user } = await picket.login({ chain: "solana" });
```

{% endtab %}
{% endtabs %}

#### Token Gating

{% hint style="info" %}
**Checkout the Getting Started Guides**

For more information on Token Gating, read the [Ethereum](https://docs.picketapi.com/picket-docs/quick-start-guides/quick-start-guides/token-gating-ethereum-evm) or [Solana](https://docs.picketapi.com/picket-docs/quick-start-guides/quick-start-guides/token-gating-solana) Token Gating Getting Started Guide
{% endhint %}

{% tabs %}
{% tab title="EVM-Compatible Chains" %}

```typescript
await picket.login({
    // any supported EVM-compatible chain
    // if omitted, defaults to "ethereum"
    chain: "ethereum",
    // restrict access to token holders
    contractAddress: "0xCONTRACT_ADDRESS", 
    // omit if any number of tokens are acceptable
    minTokenBalance: 1
});
```

{% endtab %}

{% tab title="Solana" %}

```typescript
await picket.login({
    chain: "solana",
     // Replace this the tokens you want to verify ownership for
    // the token ID is the mint associated with a SPL token
    // user needs to own the minTokenBalance of at least one of the listed token 
    tokenIds: ["78AZe2223PknLYT9mn2VCJPAsdvuB6LzFAhgQeVoxddW", "2dQG4YYunFrbJjzW6UTcUmePs8UDy5jz43H6uSCZSAcS"],
    // Replace with minimum balance you want to verify users' currently hold across all token IDs, 
    // or omit if any number of tokens is sufficient
    minTokenBalance: 1 
});
```

{% endtab %}
{% endtabs %}

### Logout

`logout` deletes the cached user's access token.&#x20;

```typescript
await picket.logout();
```

### AuthState

`authState` returns the user's current authorization state.&#x20;

If the user is logged in, this includes the user's `accessToken` and information. If the user is logged out, the `authState` will be null.&#x20;

```typescript
// assumes user is logged in
const { accessToken, user } = await picket.authState();
```

### isCurrentUserAuthorized

`isCurrentUserAuthorized` checks if the currently logged in user meets the given authorization requirements. This is commonly used for implementing [incremental auth](https://docs.picketapi.com/picket-docs/reference/concepts/incremental-authorization).

```typescript
const allowed = await picket.isCurrentUserAuthorized({
   // pass in authorization requirements
   requirements: { 
     contractAddresss: "0xCONTRACT",
   },
});
```

On success, `isCurrentUserAuthorized` will update the user's access token. The updated access token contains the user's related token balances, so all future calls `isCurrentUserAuthorized` will return true. Once a user is logged out or their session expires, their cached token balances will be cleared.

#### Forcing Revalidation

By default, all calls to `isCurrentUserAuthorized` check the user's access token token balances before re-fetching their balances from the blockchain. This avoids unnecessary network I/O and keeps the user experience as snappy as possible.&#x20;

If you know the user's related token balances have changed, you can force Picket to re-fetch their information via the `revalidation` parameter

```typescript
const allowed = await picket.isCurrentUserAuthorized({
   // pass in authorization requirements
   requirements: { 
     contractAddresss: "0xCONTRACT",
   },
   // do not use cache when checking the user's token balances
   revalidate: true,
});
```

### Connect

`connect` is a convenience function for connecting to a wallet provider with the user-friendly Picket connect modal&#x20;

{% tabs %}
{% tab title="Ethereum" %}

```typescript
// Ethereum is the default chain
const { walletAddress, signature, provider } = await picket.connect();
// it can also be passed in explicitly
const { walletAddress, signature, provider } = await picket.connect({ chain: "ethereum" });
```

{% endtab %}

{% tab title="Solana" %}

```typescript
const { walletAddress, signature, provider } = await picket.connect({ chain: "solana" });
```

{% endtab %}
{% endtabs %}

### Nonce

A `nonce` is random value generated by the Picket API to that user must sign to prove ownership a wallet address. The `login` function handles `nonce` generation and signature verification for you. You'll only need to use `nonce` if you'd like to implement your own wallet authentication flow.&#x20;

A `nonce` is unique to a project and wallet address. If a `nonce` doesn't exist for the project and wallet address, Picket will generate a new `nonce`; otherwise, Picket will return the existing nonce. A `nonce` is valid for two minutes before self-destructing.

```javascript
const { nonce, statement, format } = await picket.nonce({ 
    chain: "ethereum",
    walletAddress: "0x_WALLET_ADDRESS",
});
```

#### Statement Localization

`nonce` takes in an optional `locale` parameter, which is used to localize the signing message statement in to the given `locale` . When using the `login` function from `picket-js` or `picket-react` , the user's browser locale will automatically be passed as the `locale` for the signing message statement.

{% hint style="info" %}
**Language Codes**

`locale` must be a BCP-47 language code. To see a full list, checkout [language subtag registry](https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry)
{% endhint %}

```typescript
const { nonce, statement, format } = await picket.nonce({ 
    chain: "ethereum",
    walletAddress: "0x_WALLET_ADDRESS",
    // translate the statement to Afrikaans
    locale: "af",
});
```

### Validate

`validate` [validates an access token](https://docs.picketapi.com/picket-docs/concepts/access-tokens#validate-an-access-token). This is helpful to ensure that cached local token is still valid when the app loads. The `picket-js` library automatically validates access tokens when they are loaded from the local storage cache.&#x20;

If the access token is valid, `validate` returns the decoded claims of the access token.

```typescript
const payload = await picket.validate("ACCESS_TOKEN");
```

#### Validating Token Gating Requirements

```typescript
const payload = await picket.validate("ACCESS_TOKEN", { 
  // pass the authorization requirements
  contractAddress: "0xCONTRACT_ADDRESS",
  minTokenBalance: 1,
});
```

### Themes

The Picket Login Modal supports several themes. By default the login modal will use the `light` theme. However, you can set it to a different theme to best fit into your overall web experience.

You can set the theme when instantiating Picket:

```typescript
const picket = new Picket("YOUR_PUBLISHABLE_KEY_HERE", {
  theme: "dark",
});
```

Supported themes can be found [here](https://docs.picketapi.com/picket-docs/reference/concepts/modal-themes).

## OAuth 2.0

### Login with Redirect

`picket.loginWithRedirect` implements the [PKCE flow](https://docs.picketapi.com/picket-docs/concepts/auth-flow#oauth-2.0-authorization-code-flow-with-proof-key-for-code-exchange-pkce). In this flow the user is redirected back to a whitelisted redirect URI after authentication, so there are two steps the process 1) authenticate the user 2) handle the login redirect, also known as the login callback.

{% tabs %}
{% tab title="EVM" %}

```typescript
import { defaultLoginRedirectCallback } from "@picketapi/picket-js";

// login will redirect user to Picket authorization server
document.getElementById("login").addEventListener("click", async () => {
  try {
    await picket.loginWithRedirect();
  } catch (err) {
    console.log("failed to login", err)
  }
});

// in your callback route (defaults to the same route as your login page) 
window.addEventListener("load", async () => {
  try {
    const { appState } = await picket.handleLoginRedirect();
    defaultLoginRedirectCallback(appState);
  }
  // if successful, get user info and access token
  const { accessToken, user } = await picket.authState();
  console.log(user);
});
```

{% endtab %}

{% tab title="Solana" %}

```typescript
import { defaultLoginRedirectCallback } from "@picketapi/picket-js";

// login will redirect user to Picket authorization server
document.getElementById("login").addEventListener("click", async () => {
  try {
    await picket.loginWithRedirect({ chain: "solana" });
  } catch (err) {
    console.log("failed to login", err)
  }
});

// in your callback route (defaults to the same route as your login page) 
window.addEventListener("load", async () => {
  try {
    const { appState } = await picket.handleLoginRedirect();
    defaultLoginRedirectCallback(appState);
  }
  // if successful, get user info and access token
  const { accessToken, user } = await picket.authState();
  console.log(user);
});
```

{% endtab %}
{% endtabs %}

By default, `picket.loginWithRedirect` will redirect back to the same location (`window.location.href`) as the initial login request. If you'd like your user to be redirected to a different page, you can pass in additional options

{% tabs %}
{% tab title="EVM" %}

```typescript
await picket.loginWithRedirect({
    // restrict access to token holders
    contractAddress: "0xCONTRACT_ADDRESS",
    minTokenBalance: 1
}, {
    // change redirect location
    redirectURI: "https://my-app.com/login/callback",
});
```

{% endtab %}

{% tab title="Solana" %}

```typescript
await picket.loginWithRedirect({
    // restrict access to token holders
    tokenIds: ["TOKEN_ID_MINT"],
    minTokenBalance: 1
}, {
    // change redirect location
    redirectURI: "https://my-app.com/login/callback",
});
```

{% endtab %}
{% endtabs %}

### Login with Popup

{% hint style="warning" %}
Popup windows are often blocked by browsers. For a consistent user experience, prefer `login` or `loginWithRedirect`
{% endhint %}

`picket.loginWithPopup` is an alternative implementation of the [PKCE flow](https://docs.picketapi.com/picket-docs/concepts/auth-flow#oauth-2.0-authorization-code-flow-with-proof-key-for-code-exchange-pkce) that uses a popup rather than a redirect to securely authenticate users. The lack of redirects is convenient from a user experience perspective, but popups are often blocked by browsers, especially on mobile browsers. If you use this method, warn your users to enable popups on their browser if the flow fails.

{% tabs %}
{% tab title="EVM" %}

```typescript
await picket.loginWithPopup({
    // restrict access to token holders
    contractAddress: "0xCONTRACT_ADDRESS",
    minTokenBalance: 1
});
```

{% endtab %}

{% tab title="Solana" %}

```typescript
await picket.loginWithPopup({
    // restrict access to token holders
    tokenIds: ["TOKEN_ID_MINT"],
    minTokenBalance: 1
});
```

{% endtab %}
{% endtabs %}
