# Authentication Settings

https://dev.wisecp.com/es/authentication-settings

The seven endpoints behind the second step, location and address checks asked beyond the password.

## Overview

These seven endpoints decide what is asked **beyond the password** at sign-in: a second step, an extra check when someone arrives from an unfamiliar location, and another when they arrive from an unfamiliar address.

All three are set **separately for clients and admins**. Switching one side on leaves the other alone, so wanting both means sending both blocks.

## Reference

### Reading Two-Step Verification

get/api/v1/admin/settings/security/two-factor

`Settings/GetTwoFactor` admin

Returns which second-step methods are on and who is asked for them.

Response fields data — 4

authenticationsstring[]The keys of the methods that are on.

clientintWhether clients are asked for a second step.

adminintWhether admins are asked for a second step.

show_popupintWhether the set-up reminder is shown at sign-in.

Errors 1

insufficient_scope403The key lacks the required scope.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl 'https://panel.example.com/api/v1/admin/settings/security/two-factor' \
  -H "Authorization: Bearer $API_KEY"
```

```javascript
const res  = await fetch('https://panel.example.com/api/v1/admin/settings/security/two-factor', {
  headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();
```

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/settings/security/two-factor');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
// The switches only bite while AT LEAST ONE method is on; read them together.
$tfa  = Api::Settings()->GetTwoFactor()['data'];
$live = $tfa['admin'] === 1 && $tfa['authentications'] !== [];
```

### Writing Two-Step Verification

put/api/v1/admin/settings/security/two-factor

`Settings/UpdateTwoFactor` admin lock-out risk

Writes the methods and who is asked for them.

Body 4

authenticationsstring[]The keys of the methods to switch on. The list is written whole; a method left out is switched off.

clientintAsks clients for a second step.

adminintAsks admins for a second step.

show_popupintShows the set-up reminder at sign-in.

Response fields data

dataobjectThe settings as they stand after the write. Same shape as the read endpoint.

Errors 1

insufficient_scope403The key lacks the required scope.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl -X PUT 'https://panel.example.com/api/v1/admin/settings/security/two-factor' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"authentications":["GoogleAuthenticator"],"admin":1,"show_popup":1}'
```

```javascript
const res = await fetch('https://panel.example.com/api/v1/admin/settings/security/two-factor', {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    authentications: ['GoogleAuthenticator'],
    admin: 1,
    show_popup: 1,
  }),
});

const body = await res.json();
```

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/settings/security/two-factor');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'authentications' => ['GoogleAuthenticator'],
        'admin'           => 1,
    ]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
// The method list is written WHOLE: sending one while meaning to add turns the others off.
$tfa     = Api::Settings()->GetTwoFactor()['data'];
$methods = $tfa['authentications'];

$methods[] = 'GoogleAuthenticator';

Api::Settings()->UpdateTwoFactor(['authentications' => array_unique($methods)]);
```

### Listing the Methods

get/api/v1/admin/settings/security/two-factor/methods

`Settings/GetTwoFactorMethods` admin

Returns the second-step methods installed and which of them are on.

Response fields data[] — 4

keystringThe method key. This is what the write endpoint takes.

namestringThe method name.

descriptionstringWhat it does.

activeboolWhether it is on right now.

Errors 1

insufficient_scope403The key lacks the required scope.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl 'https://panel.example.com/api/v1/admin/settings/security/two-factor/methods' \
  -H "Authorization: Bearer $API_KEY"
```

```javascript
const res  = await fetch('https://panel.example.com/api/v1/admin/settings/security/two-factor/methods', {
  headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();
```

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/settings/security/two-factor/methods');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
// Take the method keys from HERE: an invented key is ignored without a word.
$keys = array_column(Api::Settings()->GetTwoFactorMethods()['data'], 'key');
```

### Reading Location Verification

get/api/v1/admin/settings/security/location-verification

`Settings/GetLocationVerification` admin two sides, separate

Returns what happens when someone signs in from an unfamiliar location.

Response fields data — 2

clientobject 3 fieldsThe setting on the client side.

statusintWhether location verification is on.

methodstringWhich channel the verification is asked through.

typestringHow strictly the verification is enforced.

adminobject 3 fieldsThe setting on the admin side.

statusintWhether location verification is on.

methodstringWhich channel the verification is asked through.

typestringHow strictly the verification is enforced.

Errors 1

insufficient_scope403The key lacks the required scope.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl 'https://panel.example.com/api/v1/admin/settings/security/location-verification' \
  -H "Authorization: Bearer $API_KEY"
```

```javascript
const res  = await fetch('https://panel.example.com/api/v1/admin/settings/security/location-verification', {
  headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();
```

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/settings/security/location-verification');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
// The client and admin settings are INDEPENDENT: switching one on leaves the other alone.
$loc = Api::Settings()->GetLocationVerification()['data'];
```

### Writing Location Verification

put/api/v1/admin/settings/security/location-verification

`Settings/UpdateLocationVerification` admin

Writes location verification, one side at a time.

Body 2

clientobjectThe client-side setting: status, channel and strictness.

adminobjectThe admin-side setting: status, channel and strictness.

Response fields data

dataobjectThe settings as they stand after the write. Same shape as the read endpoint: a client block and an admin block, both of them.

Errors 1

insufficient_scope403The key lacks the required scope.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl -X PUT 'https://panel.example.com/api/v1/admin/settings/security/location-verification' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"client":{"status":1,"method":"email","type":"soft"}}'
```

```javascript
const res = await fetch('https://panel.example.com/api/v1/admin/settings/security/location-verification', {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    client: { status: 1, method: 'email', type: 'soft' },
  }),
});

const body = await res.json();
```

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/settings/security/location-verification');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'client' => ['status' => 1, 'method' => 'email', 'type' => 'soft'],
    ]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
// Resolving a location depends on the LOCATION MODULE: without one the check cannot work as expected.
$loc = Api::Settings()->GetLocalisation()['data'];

if ($loc['ip_module'] !== '') {
    Api::Settings()->UpdateLocationVerification([
        'client' => ['status' => 1, 'method' => 'email'],
    ]);
}
```

### Reading Address Verification

get/api/v1/admin/settings/security/ip-verification

`Settings/GetIpVerification` admin two sides, separate

Returns what happens when someone signs in from an unfamiliar address.

Response fields data — 2

clientobject 2 fieldsThe setting on the client side.

statusintWhether address verification is on.

whiteliststringThe addresses exempt from verification.

adminobject 2 fieldsThe setting on the admin side.

statusintWhether address verification is on.

whiteliststringThe addresses exempt from verification.

Errors 1

insufficient_scope403The key lacks the required scope.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl 'https://panel.example.com/api/v1/admin/settings/security/ip-verification' \
  -H "Authorization: Bearer $API_KEY"
```

```javascript
const res  = await fetch('https://panel.example.com/api/v1/admin/settings/security/ip-verification', {
  headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();
```

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/settings/security/ip-verification');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
$ip = Api::Settings()->GetIpVerification()['data'];
```

### Writing Address Verification

put/api/v1/admin/settings/security/ip-verification

`Settings/UpdateIpVerification` admin lock-out risk

Writes address verification, one side at a time.

Body 2

clientobjectThe client-side setting: status and exempt addresses.

adminobjectThe admin-side setting: status and exempt addresses.

Response fields data

dataobjectThe settings as they stand after the write. Same shape as the read endpoint: a client block and an admin block, both of them.

Errors 1

insufficient_scope403The key lacks the required scope.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl -X PUT 'https://panel.example.com/api/v1/admin/settings/security/ip-verification' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"admin":{"status":1,"whitelist":"203.0.113.0/24"}}'
```

```javascript
const res = await fetch('https://panel.example.com/api/v1/admin/settings/security/ip-verification', {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    admin: { status: 1, whitelist: '203.0.113.0/24' },
  }),
});

const body = await res.json();
```

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/settings/security/ip-verification');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'admin' => ['status' => 1, 'whitelist' => '203.0.113.0/24'],
    ]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
// The exempt list is REPLACED by what you send: read it first and append to it.
$ip = Api::Settings()->GetIpVerification()['data'];

Api::Settings()->UpdateIpVerification([
    'admin' => [
        'status'    => 1,
        'whitelist' => $ip['admin']['whitelist'] . "\n" . $myAddress,
    ],
]);
```

## Pitfalls

> **The switch does nothing without a method**
> 
> The client and admin switches only bite while **at least one method is on**. Turning the admin second step on with an empty method list changes nothing, yet the setting reads as on and you believe you are protected. Read them together.

> **The method list is written whole**
> 
> The method list you send **replaces** the old one: sending one while meaning to add turns the others off. The second step users set up with those methods stops working at once. Read the current list and append to it.

> **Location verification leans on a module**
> 
> Working out where a sign-in came from is the location module's job. With no module installed, or with its lookup failing, the check does not behave as you expect. Confirm the location module is chosen in the localisation settings before switching this on.

> **Address verification covers you too**
> 
> Switching address verification on for admins covers **you as well**. On a connection whose address changes, every sign-in asks for the extra check, and forgetting your own address on the exempt list can leave you in an awkward spot. The exempt list is also replaced by what you send, not merged.

> **Method keys cannot be invented**
> 
> The methods to switch on are named by the keys of installed modules, and those keys come from the methods endpoint. An unrecognised key is **ignored without a word**: the request looks successful while the method stays off. Confirm through the read endpoint afterwards.

## Related Articles

- [Security Settings](https://dev.wisecp.com/en/security-settings)
- [Localisation and URLs](https://dev.wisecp.com/en/localisation-and-urls)
- [Client Registration](https://dev.wisecp.com/en/client-registration)
