# Authentication 🔐

This API supports authentication for two different kinds of use cases:

* [**Server-to-server**](#server-to-server) - backend systems, admin portals, etc.
* [**End User**](#end-user) - a patient or clinician most likely on mobile or the web.

Jump down to example [authentication patterns](#authentication-pattern)

### Server-to-server

[CardScan.ai](https://www.cardscan.ai) authenticates server-to-server (S2S) API requests using your account's API keys. A request without an API key, or with an expired, or revoked key, will cause the API to return an error.

Every account has separate keys for testing on our sandbox, or for running live in production. The sandbox API is identical to the production API.

{% hint style="warning" %}
The sandbox API is **not** HIPAA compliant and should **NOT** be used for PHI.
{% endhint %}

#### API Keys

Your API Keys are available on the Dashboard. The API Keys start with a prefix to clearly distinguish their usage.

For accessing the API in the **sandbox** environment use keys with this format:

* `sk_test_cardscan_ai_XXXXXXXXXXXXXXX`

When you are ready for **production** or **live** mode, use keys with this format:

* `sk_live_cardscan_ai_XXXXXXXXXXXXXXX`

{% hint style="info" %}
**Note:** Legacy API keys in the format `secret_test_` and `secret_live_` are still supported but new keys use the `sk_test_cardscan_ai_` and `sk_live_cardscan_ai_` format.
{% endhint %}

Read more about sandbox vs live mode on the [API Endpoints](https://docs.cardscan.ai/api#api-endpoints) page

{% hint style="warning" %}
API Keys are like passwords, keep them safe and **NEVER** use them in a client-side application.
{% endhint %}

### End User

End users on all platforms (web, mobile, etc) authenticate with the [Cardscan.ai](https://www.cardscan.ai) APIs using a `sessionToken`. This token is a short-lived JSON Web Token (JWT).

Requesting a token is done via the [Access Token](https://docs.cardscan.ai/api#get-access-token) endpoint.

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

```bash
curl --request GET 'https://sandbox.cardscan.ai/v1/access-token' \
--header 'Authorization: Bearer sk_test_cardscan_ai_XXXXXXXXXXXXXXX'
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://sandbox.cardscan.ai/v1/access-token"

headers = {
  'Authorization': 'Bearer sk_test_cardscan_ai_XXXXXXXXXXXXXXX'
}

response = requests.request("GET", url, headers=headers)
print(response.json())
```

{% endtab %}

{% tab title="Node.js" %}

```javascript
var axios = require('axios');

url = 'https://sandbox.cardscan.ai/v1/access-token'

var options = {
    headers: { 
        'Authorization': 'Bearer sk_test_cardscan_ai_XXXXXXXXXXXXXXX'
    }
}

axios.get(url, options)
  .then(function (response) {
    console.log(JSON.stringify(response.data))
  })
  .catch(function (error) {
    console.log(error);
});
```

{% endtab %}
{% endtabs %}

By default end users lose access to uploaded cards and all associated data when their session token expires. To prevent this pass in a `user_id` as a query parameter to the `/access-token` endpoint. The `user_id` parameter **must be unique** across your user base, we recommend using an email address or internal `uuid` identifier.

{% hint style="warning" %}
**WARNING:** using a non-unique **user\_id** will result in PHI exposure
{% endhint %}

```bash
curl --request GET 'https://sandbox.cardscan.ai/v1/access-token?user_id=d77176fb-be40-4884-b9bb-ca64f657804b' \
--header 'Authorization: Bearer sk_test_cardscan_ai_XXXXXXXXXXXXXXX'
```

{% hint style="info" %}
**Server-to-server** requests continue to have access to uploaded cards and associated data, even after the end user's session has expired.
{% endhint %}

### Authentication Pattern

The recommended pattern for authenticating end users is to create a [CardScan.ai](https://www.cardscan.ai) authentication endpoint on the customer's backend servers. In the diagram below the endpoint is called `/cardscan-session` and is responsible for authenticating the end user before requesting a session token from the [CardScan.ai](https://www.cardscan.ai) API.

![Auth Diagram](https://423317997-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MYkGp0C8rvjnJYLAI_u%2F-MfsJVO90jc142qskAs1%2F-MfsNfloF9y1fiSwRok7%2FAuth%20diagram.png?alt=media\&token=c753d0d4-067d-4747-a869-2f7d432fde1b)

Below are two overly simplified examples of this workflow for **Flask** and **Express**:

{% tabs %}
{% tab title="Flask Example" %}

```python
import requests
from flask_login import login_required, current_user


@app.route('/cardscan-session')
@login_required
def session():
    '''
    Generates a cardscan.ai token for the logged-in user and returns it.
    '''
    url = "https://sandbox.cardscan.ai/v1/access-token"
    params = {
        'user_id': current_user.id
    }
    headers = {
        'Authorization': 'Bearer sk_test_cardscan_ai_XXXXXXXXXXXXXXX'
    }
    response = requests.request("GET", url, params=params, headers=headers)
    response.raise_for_status()
    payload = response.json()

    return jsonify(payload)

```

{% endtab %}

{% tab title="Node.js Express Example" %}

```javascript
const router = express.Router();
var axios = require('axios');

router.get('/cardscan-session', (req, res) => {

    url = 'https://sandbox.cardscan.ai/v1/access-token'

    var options = {
        headers: { 
            'Authorization': 'Bearer sk_test_cardscan_ai_XXXXXXXXXXXXXXX'
        },
        params: {
            'user_id': req.auth.user
        }
    }

    axios.get(url, options)
      .then(function (response) {
        res.setHeader('Content-Type', 'application/json');
        res.end(JSON.stringify(response.data));
      })
      .catch(function (error) {
        //Handle error
        res.status(error.response.status);
        res.end(error.response.data.message);
    });
});

```

{% endtab %}
{% endtabs %}

Once a `session` has been generated, it can be used to initialize the SDK and UI Components, or used to call the API directly. This allows the end user's browser or mobile device to safely and securely connect with the [CardScan.ai](https://cardscan.ai/) servers.

{% tabs %}
{% tab title="Swift Client" %}

```swift
private func didTapScanCard(_ sender: UIButton) {

    button.showLoaderAboveImage(userInteraction:true)

    var request = URLRequest(url: URL(string: "https://{{YOUR_SERVER_BASE_URL}}/cardscan-session")!)
    request.httpMethod = "GET"
    request.setValue("application/json", forHTTPHeaderField: "Accept")
      
    URLSession.shared.dataTask(with: request) { [weak self] data, response, error in
        
           ///Extract session from server response
            guard error == nil,
              let data = data,
              let json = try? JSONDecoder().decode([String: String].self, from: data),
            let session = json["session"] else {

            //handle errors calling server. :-(
            print("Error calling server: ", error as Any)
            return
        }
        
        ///Trigger presentation of CardScannerView with user's session token.
        DispatchQueue.main.async { [weak self] in
            self?.presentCardScanner(userToken: session)
        }
    }.resume()
}

private func presentCardScanner(userToken: String) {
    
    ///Create CardScannerView with user session token
    let cardScannerViewController = CardScannerViewController(userToken: userToken, live: true)
    
    cardScannerViewController.present(from: self, animated:true) { result in 
        print(result)
    }
}
```

{% endtab %}

{% tab title="React Client" %}

```jsx
import { useEffect, useState } from "react";
import { CardScanView } from "@cardscan.ai/insurance-cardscan-react";

const Onboarding = () => {
  const [token, setToken] = useState("");
  
  const onSuccess = (card: any) => {
    console.log("success!");
  };
  
  const loadScanView = () => {  
  
    fetch("https://{{YOUR_SERVER_BASE_URL}}/cardscan-session", {
      method: "POST",
    })
      .then((res) => res.json())
      .then((data) => {
        setToken(data.Token);
      })
      .catch((err) => console.log(err));
  };
    
    return (
      <div>
        { (token.trim() == "") ?
        <button onClick={loadScanView}>Start Scanning</button>;
        : 
        <CardScanView
          live={false}
          sessionToken={token}
          onSuccess={onSuccess}
        />
      });
};

export default Onboarding;
```

{% endtab %}

{% tab title="Kotlin Client" %}

```kotlin
class OnboardingActivity : AppCompatActivity(), CardScanActivityResult {

    lateinit var cardScanResultLauncher : ActivityResultLauncher<Intent>

    private fun getSession(callback: (session: String) -> Unit) {
        val httpAsync = "https://{{YOUR_SERVER_BASE_URL}}/cardscan-session"
            .httpPost()
            .responseJson { _, _, result ->
                //check for errors :)
                val jsonObject = result.get().obj()
                val session = jsonObject["session"] as String
                callback(session)
            }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_onboarding)

        
        cardScanResultLauncher =  registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
           CardScanActivity.processResult(this@OnboardingActivity, result)
       }

        findViewById<View>(R.id.scanCardButton).setOnClickListener { _ ->
            getSession { session ->
                //trigger the loading of CardScanActivity with user's session token
                CardScanActivity.start(
                    activity = this,
                    resultLauncher = cardScanResultLauncher,
                    sessionToken = session
                )
            }

        }
    }

    override fun scanSuccess(card: CardData) {
        Log.d("CardScan", "ScanSuccess $card")
    }

}
```

{% endtab %}
{% endtabs %}
