# Credential Store SDK

Below are the SDK methods tied to a credential and how they are called.

## Registering

### RegisterRefresh

This is the primary method for registering a refresh action.&#x20;

```csharp
CredentialStore.RegisterRefresh("SuperCredential", (o) => {

        //Call third party API
        //Reach out to database
        //Request keys from AWS
        //Do anything you need to get authorization details

        //Then return either a good credential:
        return new CredentialStoreItem() { 
            Expiration = DateTimeOffset.Now.AddMinutes(60),
            Authorization = "ABCDEFG",
            Scope = "offline.access",
            StoreA = "Custom ValueStore"
        };

        //Or a faulted one:
        return new FaultedCredentialStoreItem("External ERROR", new Exception("Exception thrown while trying to get credential!!!"), retry: true);
    });
```

The **`(o) =>`** action callback is the **`refreshParam`** optionally supplied by calling **`GetCredential()`**. It is not required, but can be optionally used for custom logic

### RegisterConnectionString

To register a connection string as a credential, which is a great way to support hot-reloading:

```csharp
PerigeeApplication.ApplicationNoInit("ConnectionStrings", (c) => {

    //Register the connection string, or with the shortcut
    c.RegisterConnectionString("DB", "devServer");
    CredentialStore.RegisterConnectionString("DB", "devServer");
    
});
```

The <mark style="color:red;">`AppSettings.json`</mark> file:

```json
"ConnectionStrings": {
      "devServer": "data source=host;initial catalog=database; User Id=user; Password=abc"
}
```

### RegisterSalesForceJWT

This is the way you register a SalesForce JWT Authentication method.&#x20;

This is automatically created as part of the SalesForce client included in Perigee. See the [SalesForce page](https://docs.perigee.software/core-modules/event-sources/watchers/salesforce) for more info.

```csharp
CredentialStore.RegisterSalesForceJWT("SFJWT", 
"username", "consumerKey", 
new X509Certificate2("SF.pfx", "ABCD123"), 
"login");
```

## Configuration

### Configure

Configure is how we setup credential encryption, credential backup and restore policies.

#### Encryption

Supplying AES Keys will tell Perigee to use an encrypted credential store file. Even if you're already running without encryption, Perigee will detect the request to encrypt and convert the credential file before starting up.&#x20;

#### Backup and restore

The **`initializationCallback`** and **`writeCallack`** can be used to effectively back up and restore your credential file remotely.&#x20;

Every time a new credential file is written the **`writeCallback`** is called, this is your opportunity to store that elsewhere, even in a database blob object or on the cloud.&#x20;

Every time the application starts up and **NO credential file is present**, the **`initializationCallback`** is called. This allows you to restore a credential file written elsewhere.

Here's an example of setting up your Perigee Application with credential encryption, backup and restore.&#x20;

```csharp
PerigeeApplication.ApplicationNoInit("Credentials", (c) => {
  //If calling configure, do so FIRST in the callback.
  
  // Call Configure, Perigee will auto convert non encrypted store to encrypted store
  CredentialStore.Configure(
    initializationCallback: () =>
    {
        //When there are no credentials, restore from remote backup (in this case another file on the hard drive, but it could be a database too)
        return CredentialStore.ConvertBytesToDictionary(File.ReadAllBytes(@"C:\temp\credentialbaks\credentials.pce"), AESKey, AESIV);
    }, 
    writeCallack: (bytes) =>
    {
        //Put the credential bytes anywhere else!
        FileRevisionStore.SaveFile(@"C:\temp\credentialbaks\credentialsBackup.pce", bytes);
    }, 
    AES32Key: AESKey, //Supplying AESKey and AESIV will encrypt the credentials
    AES16IV: AESIV);  //Supplying AESKey and AESIV will encrypt the credentials
});

//To get a new AESKey: AesCrypto.GetNewRandom(32, false)
//To get a new AESIV: AesCrypto.GetNewRandom(16, false)
//Or generate AES256 keys on your own!
```

{% hint style="warning" %}
&#x20;Configure should be called **FIRST** after instanciating a perigee application.
{% endhint %}

{% hint style="success" %}
To remove encryption after encrypting, see the [DecryptAndRevert](#decryptcredentialfileandrevert) call below
{% endhint %}

### CredentialsToBytes

This converts all the current credentials to a byte array.

If AES keys are provided, the bytes are encrypted.

If they are not provided, the bytes are compressed only.

```csharp
CredentialStore.CredentialsToBytes(string AES32Key, string AES16IV)
```

### ConvertDictionaryToBytes / ConvertBytesToDictionary

This converts a dictionary of credentials to an optionally encrypted byte array.&#x20;

If AES keys are provided, the bytes are encrypted.

If they are not provided, the bytes are compressed only.

```csharp
CredentialStore.ConvertDictionaryToBytes(
Dictionary<string, CredentialStoreItem> dictionary, 
string AES32Key, string AES16IV)
```

The inverse of this operation works exactly the same way:

```csharp
CredentialStore.ConvertBytesToDictionary(
byte[] bytes, 
string AES32Key, string AES16IV)
```

### DecryptCredentialFileAndRevert

If you're attempting to decrypt the encrypted credential file and revert back to using a non encrypted file, please call this before application start once, then remove. It will revert back the encryption in place.

The AES Key and IV will need to be supplied.

The altPath is a pointer to revert a different encrytped credential file than the default path. It's optional to supply this value

```csharp
DecryptCredentialFileAndRevert(
string AES32Key, string AES16IV, 
string altPath = null)
```

## Retrieve

### GetCredential

To get a credential again at a later time, call GetCredential(). It will go through the process of synchronously locking and retrieving that credential and re-authorizing if needed.

```csharp
//Use default settings
CredentialStore.GetCredential("SuperCredential"); 

//Set all settings on retrieval 
CredentialStore.GetCredential("SuperCredential", 
    maxRetries: 3, 
    retryMS: 1000, 
    expireTimeBufferSeconds: 600,
    disableRefresh: false, 
    object refreshParam: "Token");
```

The optional parameters are as follows:

* <mark style="color:blue;">`maxRetries`</mark> - If a refresh action returns a faulted credential then it defines the number of times a credential can attempt to "re-retrieve" it.
* <mark style="color:blue;">`retryMS`</mark> - How many milliseconds between retry attempts.
* <mark style="color:blue;">`expireTimeBufferSeconds`</mark> - How many seconds to buffer the expiration so an early refresh is called. Very useful to prevent a lapse in operation.
* <mark style="color:blue;">`disableRefresh`</mark> - If true, it will ONLY return the credential if it exists and it will not refresh or renew it. Useful for pulling expired credentials.
* <mark style="color:blue;">`refreshParam`</mark> - an object that can be passed to the registered refresh. Useful for supplying a one time use authorization token, or additional details to customize the way the refresh happens.

### GetCredential\_ConnectionString

To get a credential of type ConnectionStringCredentialStoreItem:

```csharp
CredentialStore.GetCredential_ConnectionString("DB");
```

### GetCredentialsByAuthorization

If you want to know what credentials have/share an authorization property:

```csharp
var ienumerable = CredentialStore.GetCredentialsByAuthorization("authorizationTokenOrCode");
```

### GetRefreshToken

Get a refresh token from a credential, even if that credential is expired.

```csharp
CredentialStore.GetRefreshToken("credName");
```

### PeekCredential

Will retrieve a credential without refreshing it, if it is invalid.&#x20;

```csharp
CredentialStore.PeekCredential("credName");
```

### RefreshAuthorizationCode

Passes an authorization code the named refresh.&#x20;

```csharp
CredentialStore.RefreshAuthorizationCode("credName", "authCode");

//This is a shorthand helper method that is simply: 
//    GetCredential(name, 2, refreshParam: code)
```

### Awaiting

You can await valid credentials, which is useful when waiting on a refresh or awaiting user interaction to authenticate a client. This method is **`async`** and can be awaited.

It pulls the credential once attempting to refresh it, if that fails then pulls it again on a timer with **`disableRefresh:true`**, meaning it won't try to refresh the credential and will wait on a valid credential.

```csharp
await CredentialStore.AwaitValidCredential("name", CancelToken);
```

## Contains / Query

### ContainsRefresh

Returns true if a refresh action is registered for this credential name.

```csharp
CredentialStore.ContainsRefresh("credName");
```

### ContainsCredential

Returns true if the credentials exists, OR a refresh action is tied to that name.

```csharp
CredentialStore.ContainsCredential("credName");
```

### Query credentials with predicate&#x20;

You can query all credentials without calling the refresh actions by using the `ByPredicate` method.&#x20;

```csharp
CredentialStore.GetCredentialsByPredicate(f => f.Name.Contains("Test"));
```

## Invalidation

### InvalidateCredential

Invalidation simply caused the credential's expiration to be set to `DateTimeOffset.MinValue` and then forces an immediate persist. Any future call to `GetCredential()` will force a refresh since it is now force-expired.

```csharp
CredentialStore.InvalidateCredential("credName");
```

## CredentialStoreItem

The Item has several properties you may use:

```
Name => Name of the credential. (don't set this!) 
Key => A key value.
Value=> A "Value" value.
Scope => If there is a scope associated with the credential.
Environment => What environment, or host does this belong to?
Authorization => a string, authorization, code, or token.
RefreshToken => If the OAUTH2 flow contains a refresh token, store it here.
Attributes => a dictionary of custom attributes, feel free to add whatever here.
Expiration => DateTimeOffset of the expiration of this credential. 

//Custom Store fields
StoreA => A custom field for any value
StoreB => A custom field for any value
StoreC => A custom field for any value

//For faulted
isFaulted => If the credential is a "Faulted" type
FaultReason => Fill this out with more information about why it was faulted
ExceptionGiven => If there was an exception thrown that is associated with the fault.

```

### isExpired(int bufferSeconds = 300)

You can check if a credential is expired, or is about to expire by supplying the `bufferSeconds` parameter.

```csharp
// Will check if the credential is or will expire in the next 2 minutes
item.isExpired(120); 
```

### DecodeJWT(string token)

This will decode a JWT token into the JSON string of the body.

```csharp
item.DecodeJWT(item.Authorization); 
```
