> For the complete documentation index, see [llms.txt](https://docs.extrahorizon.com/javascript-sdk/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.extrahorizon.com/javascript-sdk/features/authentication.md).

# Authentication

### Snippet for authentication flow (Oauth2)

Each time the SDK refreshes the `accessToken` the `freshTokensCallback` is called with the response. You can store this data in `localStorage` or any other persistant data store. When you restart your application, you can check the data store for a `refreshToken` and use that to authenticate with the SDK.

```ts
import { createOAuth2Client } from "@extrahorizon/javascript-sdk";

const exh = createOAuth2Client({
  host: "",
  clientId: "",
  freshTokensCallback: (tokenData) => {
    localStorage.setItem("refreshToken", tokenData.refreshToken);
  },
});

try {
  const refreshToken = await localStorage.getItem("refreshToken");

  if (refreshToken) {
    await exh.auth.authenticate({
      refreshToken,
    });
  } else {
    // redirect to /login
  }
} catch (error) {
  localStorage.removeItem("refreshToken");
  // redirect to /login
}
```

### Snippet for authentication flow (Oauth1)

You need to capture the response from the `authenticate` function when logging in with `email` / `password` so that subsequent SDK initializations such as app restarts can use the `key` / `secret` combination stored in persistent data storage to authenticate the current user.

```ts
import { createOAuth1Client } from "@extrahorizon/javascript-sdk";

const exh = createOAuth1Client({
  host: "https://api.dev.exh-sandbox.extrahorizon.io",
  consumerKey: "",
  consumerSecret: "",
});

try {
  const tokenData = await localStorage.getItem("tokenData");

  if (tokenData) {
    await exh.auth.authenticate({
      token: tokenData.key,
      tokenSecret: tokenData.secret,
    });
  } else {
    // redirect to /login
    const result = await exh.auth.authenticate({
      email: "",
      password: "",
    });
    localStorage.setItem("tokenData", result);
  }
} catch (error) {
  localStorage.removeItem("tokenData");
  // redirect to /login
}
```

### Proxy client

The package export a client you can use in combination with a proxy service. The client will throw a typed error in case you need to redirect to the login page.

```ts
import { createProxyClient } from "@extrahorizon/javascript-sdk";

const loginPageUrl = "https://yourDomain.com/login";

(async () => {
  try {
    const exh = createProxyClient({ host: "api.dev.exh-sandbox.extrahorizon.io" });
    await exh.users.me();
  } catch (error) {
    if (
      error instanceof UserNotAuthenticatedError ||
      error instanceof OauthTokenError
    ) {
      redirectToUrl(`${loginPageUrl}/?redirect=${window.location.url}`);
    }
  }
})();
```

#### Local setup

If you want to use the proxy sdk locally, you need to make some changes to your local setup.

* Add `127.0.0.1 local.yourdomain.com` to your `/etc/hosts` file (or if you are using Windows `c:\Windows\System32\Drivers\etc\hosts`)
* Start your server with https enabled.
  * For Mac/Linux, this can be done by running `HTTPS=true yarn start`.
  * For Windows, you have to add `HTTPS=true` to your user environment. Once the variable has been set, run `yarn start`.
* Open your browser `https://local.yourdomain.com:3000/` and skip the security warning.

#### Snippet for stored credentials

When you already use the `exh/cli` tool, you can use this snippet to initialize. More info: <https://docs.extrahorizon.com/cli/setup/credentials>

```ts
import fs from "fs";
import path from "path";
import {
  parseStoredCredentials,
  createOAuth1Client,
} from "@extrahorizon/javascript-sdk";

const EXH_CONFIG_FILE = path.join(process.env.HOME, "/.exh/credentials");

const readFile = () => {
  try {
    return fs.readFileSync(EXH_CONFIG_FILE, "utf-8");
  } catch (err) {
    throw new Error(
      `Failed to open credentials file. Make sure they are correctly specified in ${EXH_CONFIG_FILE}`
    );
  }
};

try {
  const credentials = parseStoredCredentials(readFile());
  const exh = createOAuth1Client({
    consumerKey: credentials.API_OAUTH_CONSUMER_KEY,
    consumerSecret: credentials.API_OAUTH_CONSUMER_SECRET,
    host: credentials.API_HOST,
  });
  await exh.auth.authenticate({
    token: credentials.API_OAUTH_TOKEN,
    tokenSecret: credentials.API_OAUTH_TOKEN_SECRET,
  });
} catch (error) {
  console.log(error);
}
```

### Other examples

#### OAuth1

**Token authentication with optional skip**

The `skipTokenCheck` saves \~300ms by skipping validation on your `token` and `tokenSecret`.

```ts
import { createOAuth1Client } from "@extrahorion/javascript-sdk";

const exh = createOAuth1Client({
  host: "sandbox.extrahorizon.io",
  consumerKey: "",
  consumerSecret: "",
});

await exh.auth.authenticate({
  token: "",
  tokenSecret: "",
  skipTokenCheck: true,
});
```

**Email authentication**

```ts
import { createOAuth1Client } from "@extrahorizon/javascript-sdk";

const exh = createOAuth1Client({
  host: "sandbox.extrahorizon.io",
  consumerKey: "",
  consumerSecret: "",
});

await exh.auth.authenticate({
  email: "",
  password: "",
});
```

#### OAuth2

**Password Grant flow**

```ts
import { createOAuth2Client } from "@extrahorizon/javascript-sdk";

const exh = createOAuth2Client({
  host: "",
  clientId: "",
});

await exh.auth.authenticate({
  password: "",
  username: "",
});
```

**Authorization Code Grant flow with callback**

* Generating an Authorization Code is out of scope for this snippet, but generally:
  * Your application has a login/authorization page
  * It allows the user to login to your application
  * Shows the information about the (other, 3rd party) application requesting access
  * After consent to give access to the user its account, redirects the user to the application
* The (3rd party) application then receives the Authorization Code in the query parameters
* Capture the query params on the redirect uri
* Authenticate with the `code` query param

```ts
import { createOAuth2Client } from "@extrahorizon/javascript-sdk";

const exh = createOAuth2Client({
  host: "",
  clientId: "",
});

await exh.auth.authenticate({
  code: "",
});
```

**Refresh Token Grant flow**

```ts
import { createOAuth2Client } from "@extrahorizon/javascript-sdk";

const exh = createOAuth2Client({
  host: "",
  clientId: "",
});

await exh.auth.authenticate({
  refreshToken: "",
});
```

**Password Grant flow with two-step MFA in try / catch**

```ts
import {
  createOAuth2Client,
  MfaRequiredError,
} from "@extrahorizon/javascript-sdk";

const exh = createOAuth2Client({
  host: "",
  clientId: "",
});

try {
  await exh.auth.authenticate({
    password: "",
    username: "",
  });
} catch (error) {
  if (error instanceof MfaRequiredError) {
    const { mfa } = error;

    // Your logic to request which method the user want to use in case of multiple methods
    const methodId = mfa.methods[0].id;

    await exh.auth.confirmMfa({
      token: mfa.token,
      methodId,
      code: "", // code from ie. Google Authenticator
    });
  }
}
```

**Confidential Applications**

If you are using a confidential application in combination with React-Native. The SDK will add `btoa` function to your global scope. See <https://github.com/ExtraHorizon/javascript-sdk/issues/446>

```ts
const exh = createClient({
  host: "https://api.dev.exh-sandbox.extrahorizon.io",
  clientId: "",
  clientSecret: "",
});
```

![Refresh](/files/GUa5PChwwfSguBThrW88)

### Creating applications

#### Example

If you want to create an application can you use generic to determine the correct application and application version type.

ie. creating an OAuth1 application with a version.

```ts
// Will return OAuth1Application type
const app = await exh.auth.applications.create({
  type: "oauth1",
  name: "test",
  description: "test",
});

// Will return OAuth1ApplicationVersion type
const version = await exh.auth.applications.createVersion<typeof app>(app.id, {
  name: "1.0.0",
});
```

#### Typeguards

If you need a typeguard, you can use the following snippets.

```ts
import {
  Application,
  ApplicationVersion,
  OAuth1Application,
  OAuth1ApplicationVersion,
} from "@extrahorizon/javascript-sdk";

function isOAuth1Version(
  version: ApplicationVersion
): version is OAuth1ApplicationVersion {
  return `consumerKey` in version;
}

function isOAuth1(app: Application): app is OAuth1Application {
  return !("redirectUris" in app);
}

const { data: apps } = await exh.auth.applications.get();
apps.filter(isOAuth1).forEach((app) => {
  // app will have type OAuth1Application
});
```
