# Experience Cloud Code and Credentials (/docs/auth/flows/experience-cloud-code-credentials)



Salesforce Headless Identity's Authorization Code and Credentials flow lets an
Experience Cloud customer or partner authenticate inside an off-platform UI while
Salesforce performs the identity and authorization work behind the scenes.

<SalesforceReference title="Authorization Code and Credentials Flow for Private Clients" href="https://help.salesforce.com/s/articleView?id=sf.remoteaccess_authorization_code_credentials_flow.htm&language=en_US&type=5" />

<SalesforceReference title="Authorization Code and Credentials Flow for Public Clients" href="https://help.salesforce.com/s/articleView?id=sf.remoteaccess_authcodecreds_singlepageapp.htm&language=en_US&type=5" />

## Named-user login [#named-user-login]

```ts
import { SalesforceAuth } from "@kysoql/auth";

export async function buildHeadlessLogin(username: string, password: string) {
  const auth = new SalesforceAuth({
    loginUrl: "https://customers.example.my.site.com/customers",
    clientId: "external-client-app-id",
  });
  const pkce = await auth.generatePkcePair();
  const request = auth.codeCredentialsAuthorization({
    redirectUri: "https://app.example.com/oauth/callback",
    username,
    password,
    codeChallenge: pkce.challenge,
  });

  return { request, verifier: pkce.verifier };
}
```

Send `request.url` with `request.init` in the environment that owns the headless
login. The builder uses `redirect: "manual"` so credentials are not automatically
forwarded through a redirect.

Named-user credentials default to HTTP Basic for the authorization request. Set
`credentialsPlacement: "body"` when the Salesforce application is configured for
POST-body credentials.

## Headless user discovery [#headless-user-discovery]

```ts
import { SalesforceAuth } from "@kysoql/auth";

export function buildDiscoveredLogin(identifier: string, password: string) {
  const auth = new SalesforceAuth({
    loginUrl: "https://customers.example.my.site.com/customers",
    clientId: "external-client-app-id",
  });

  return auth.codeCredentialsAuthorization({
    redirectUri: "https://app.example.com/oauth/callback",
    loginHint: identifier,
    password,
    customData: "tenant=customer-portal",
  });
}
```

User discovery uses POST-body `login_hint`, `password`, and optional `customdata`
as required by Salesforce.

<SalesforceReference title="Headless Login Without a Username" href="https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_headless_discovery.htm&language=en_US&type=5" />

## Exchange the returned code [#exchange-the-returned-code]

When the flow returns an authorization code, exchange it against the same
Experience Cloud site base URL. Kysoql preserves configured site paths when it
appends Salesforce OAuth endpoints.

```ts
import { SalesforceAuth } from "@kysoql/auth";

export function exchangeHeadlessCode(code: string, verifier: string) {
  const auth = new SalesforceAuth({
    loginUrl: "https://customers.example.my.site.com/customers",
    clientId: "external-client-app-id",
  });

  return auth.exchangeAuthorizationCode({
    code,
    redirectUri: "https://app.example.com/oauth/callback",
    codeVerifier: verifier,
  });
}
```

For guest identity and UVID handling, use
[Experience Cloud guest authorization](/docs/auth/flows/experience-cloud-guest).
