Advanced Usage
    • 01 Sep 2024
    • 4 Minutes to read
    • Dark
      Light
    • PDF

    Advanced Usage

    • Dark
      Light
    • PDF

    Article summary

    Authentication

    v5

    Using clientId and clientSecret:

    import {Client} from 'plainid-pdp-sdk/v5'
    
    const options = <ClientConfigInterface>{
        url: 'https://demo.plainid.cloud',
        clientId: 'P6POYCA8QY5', clientSecret: 'XRp8OXVugWlnDZjP8', authorizerId: 'APA9G5HV7WXN9C'
    } 
    const client: ClientInterface = new Client(options)
    

    v3

    Using clientId and clientSecret

    import {Client} from 'plainid-pdp-sdk/v3'
    
    const config = <ClientConfigInterface>{
        url: 'https://demo.plainid.cloud',
        clientId: 'P6POYCA8QY5', clientSecret: 'XRp8OXVugWlnDZjP8', authorizerId: 'APA9G5HV7WXN9C'
    } 
    const client = new Client(config)
    

    Using JWT:

    If included in the JWT payload, the values for clientSecret and entityId are optional for this scenario and will be used from there.

    import {Client} from 'plainid-pdp-sdk/v3'
    
    const config = <ClientConfigInterface>{
        url: 'https://demo.plainid.cloud',
        clientId: 'P6POYCA8QY5', authorizerId: 'APA9G5HV7WXN9C'
    } 
    const client = new Client(config)
    
    const options = <AccessDecisionOptionsInterface>{
        headers: {
            'Authorization': 'Bearer J.W.T'
        }
    }
    
    const response: ResolutionResponse = await client.resolutionDecision(options).evaluate(payload)
    

    Interfaces

    interface ClientConfigInterface {
        url: string             // PDP url
        clientId: string        // client id of needed scope
        clientSecret?: string   // client secret of needed scope
        authorizerId: string    // authorizer id
    }
    
    interface ClientInterface {
        getConfig(): ClientConfigInterface;
        resolutionDecision(options: ResolutionDecisionOptionsInterface): ResolutionDecisionInterface;
        accessDecision(options: AccessDecisionOptionsInterface): AccessDecisionInterface;
    }
    

    Access Decision Request (v5)

    Important

    The path in the following Access Decision payload example is an absolute path that begins with a /. For more information, see Managing API Mappers.

    import {Client} from 'plainid-pdp-sdk/v5'
    const payload = <AccessDecisionPayloadInterface>{
        url: 'https://example.com/full/path?param1=value11&param1=value12&param2=value2#some_chapter',
        method: 'POST',                             
        headers: { 'header-name': 'header value' },
        body: { some: 'data' },
        meta: { runtimeFineTune: { includeIdentity: 'true' } }
    }
    
    const config = <AccessDecisionOptionsInterface>{
        maxAttempts: 2,
        delay: 100,
        headers: {'some-header': 'some value'},
    }
    const accessDecision: AccessDecisionInterface = client.accessDecision(config)
    const response: AccessDecisionResponseInterface = await accessDecision.evaluate(payload)
    

    Interfaces

    interface AccessDecisionPayloadInterface {
        url: string                                 // HTTP authorizing request url (required)
        method?: string                             // HTTP request method: POST, GET, PUT, PATCH, DELETE, ...
        headers: Record<string, string | string[]>  // HTTP request headers
        body?: any                                  // HTTP request body
        meta?: Record<string, any>                  // Additional parameters
        // Inside `meta` can be used the attribute `runtimeFineTune: Record<string, string>` - Any request attribute understandable by PDP (Runtime)
    }
    
    interface AccessDecisionOptionsInterface {
        maxAttempts?: number                        // Maximum number of attempts to retry the request. Default: `1`.
        delay?: number                              // Delay between retries in milliseconds. Default: `0`.
        schema: 'http' | 'https'                    // PDP request schema. Default: `https`.
        headers?: Record<string, string>            // Additional headers for PDP commucication.
    }
    
    interface AccessDecisionResponseInterface {
        statusCode: number                          // http response status code
        headers: Record<string, string>             // http response headers
        body: string                                // http raw response body
        access: boolean | null                      // acess decision
    }
    
    interface AccessDecisionInterface {
        getClient(): ClientInterface;
        getOptions(): AccessDecisionOptionsInterface;
        evaluate(payload: AccessDecisionPayloadInterface): Promise<AccessDecisionResponseInterface>;
    }
    

    Resolution Decision Request (v3)

    import {Client, AccessDecisionPayloadInterface, ResolutionResponse} from 'plainid-pdp-sdk/v3'
    const payload = <AccessDecisionPayloadInterface> {
        entityId: 'john.doe@gmail.com',
        entityTypeId: 'User',
        remoteIp: '1.2.2.1',
        includeAssetAttributes: true,
    }
    const options = <AccessDecisionOptionsInterface>{
        maxAttempts: 3,
        schema: 'http',
        delay: 100,
        headers: {
            'x-custom-header': 'custom value'
        },
        resolutionConfig: {
            resolutionAttributeProjectId: 'projectid',
            resolutionAttributeDataset: 'dataset',
            resolutionAttributeTable: 'table',
            resolutionAttributeColumn: 'column',
        }
    }
    
    const response: ResolutionResponse = await client.resolutionDecision(options).evaluate(payload)
    

    Interfaces

    interface ResolutionDecisionOptionsInterface {
        maxAttempts?: number                            // Maximum number of attempts to retry the request. Default: `1`.
        schema: 'http' | 'https'                        // PDP request schema. Default: `https`.
        delay?: number                                  // Delay between retries in milliseconds. Default: `0`.
        headers?: Record<string, string>                // Additional headers for PDP commucication.
        resolutionConfig?: ResolutionConfigInterface    // Resolution config to be used with parsing the PDP response
    }
    
    interface ResolutionConfigInterface {
        resolutionAttributeProjectId?: string
        resolutionAttributeDataset?: string
        resolutionAttributeTable?: string
        resolutionAttributeColumn?: string
        resolutionAttributeMaskAs?: string
        resolutionAttributeMaskFilter?: string
        resolutionMaskingResourceTypeName?: string
    }
    
    interface ResolutionDecisionResponseInterface {
        statusCode: string                              // http status code of the response
        headers: Map<string, string>                    // http headers of the response
        body: string                                    // http raw body of the response
        resolution: Resolution | null                   // parsed PDP restrictions and helpers
    }
    
    interface Resolution {
        getConfig(): Record<string, any>
        getAllRestrictions(): Map<string, Restrictions>
        getResourceRestrictions(fullResourceName: string): Restrictions
        getAllTables(): Set<string>
        isAssetMasked(fullResourceName: string, assetName: string): boolean
        isAssetAllowed(fullResourceName: string, assetName: string): boolean
        existAsset(fullResourceName: string, assetName: string): boolean
        existAllowedAssets(fullResourceName: string): boolean
    }
    
    interface Restrictions {
        FULL_CONDITION_FIELD_NAME: boolean
        FIELD_APOSTROPHES: [string, string]
        isEmpty(): boolean
        mergeWith(restrictions: Restrictions | undefined): Restrictions
        addAllowedConditionSql(condition: string): Restrictions
        addDeniedConditionSql(condition: string): Restrictions
        getConditionSql(): string   // (('Country' LIKE '%United%') AND ('balance' < 1000))
        getConditionFields(): Set<RestrictionsConditionField>
        getIsAccessEmpty(): boolean
        setIsAccessEmpty(isAccessEmpty: boolean): this
        addAsset(asset: RestrictionsAsset): Restrictions
        getAssets(): Map<String, RestrictionsAsset>
        getAsset(assetName: string): Optional<RestrictionsAsset>
    }
    
    interface RestrictionsConditionField {
        name: string                // field name, ex: "balance"
        type: string                // field type, ex: "NUMERIC", "STRING"
    }
    
    interface RestrictionsAsset {
        fullResourceName: string    // full resource name, ex: "dbName.tableName"
        name: string                // column name, ex: "balance"
        maskAs: string              // mask for the column, ex: "<censored>"
    }
    

    All conditions in one SQL where clause

    Restrictions.FULL_CONDITION_FIELD_NAME = true
    Restrictions.FIELD_APOSTROPHES = ['', '']
    const response = await client.resolutionDecision(options).evaluate(payload)
    const allRestrictions = response.resolution.getAllRestrictions()
    const whereClause = Array.from(allRestrictions.values()).map(restriction => restriction.getConditionSql()).join(' AND ')
    console.log(whereClause) // ((mydb.members.Country LIKE '%United%') AND (mydb.accounts.balance < 1000))
    

    Error handling

    Access Decision response (v5)

    try {
        const response = await client.resolutionDecision(options).evaluate(payload)
    } catch (err) {
        console.error(err.message)  // Example: "Error: getaddrinfo ENOTFOUND api.example.com"
    }
    

    Interfaces

    interface SdkError {
        id: string                      // unique id of the error
        code: string                    // error code
        name: string                    // code name
        message: string                 // message
        args?: Record<string, string>   // additional arguments
    }
    

    Resolution Decision response (v3)

    try {
        const response = await client.resolutionDecision(options).evaluate(payload)
    } catch (err) {
        console.error(err.message)  // Example: "Error: getaddrinfo ENOTFOUND api.example.com"
    }
    

    Troubleshooting

    Error: self-signed certificate in the certificate chain

    You can fix this issue using NODE_TLS_REJECT_UNAUTHORIZED=0 in the terminal, or insert the following line within the JS file:

    process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0;
    
    Note

    This is a flag which helps avoid a rejection of an unauthorized (self-signed) certification. It is not recommended to use in a production environment.


    Was this article helpful?

    What's Next