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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.extrahorizon.com/javascript-sdk/features/authentication.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
