Skip to content

feat: Add Okta ODIC support #99

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions src/client/oauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export type OauthProvider =
| "facebook"
| "jumpcloud"
| "twitch"
| "okta"


export const oauth = (
options: BaseOptions,
Expand All @@ -35,9 +37,13 @@ export const oauth = (
isError: true,
}

const base = process.env.NEXT_PUBLIC_SERVER_URL
const authUrl = `${base}/api/${options.name}/oauth/authorization/${provider}?clientOrigin=${encodeURIComponent(window.location.origin + `#${channelId}`)}`

let authUrl = `/api/${options.name}/oauth/authorization/${provider}?clientOrigin=${encodeURIComponent(window.location.origin + `#${channelId}`)}`

if (process.env.NEXT_PUBLIC_SERVER_URL) {
authUrl = `${process.env.NEXT_PUBLIC_SERVER_URL}${authUrl}`
}


const width = 600
const height = 700
const left = window.screenX + (window.outerWidth - width) / 2
Expand Down
7 changes: 6 additions & 1 deletion src/core/protocols/oauth/oidc_callback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,12 @@ export async function OIDCCallback(
.discoveryRequest(issuer_url, { algorithm })
.then((response) => oauth.processDiscoveryResponse(issuer_url, response))

const params = oauth.validateAuthResponse(as, client, current_url)
const params = oauth.validateAuthResponse(
as,
client,
current_url,
providerConfig?.params?.state || undefined,
)

const grantResponse = await oauth.authorizationCodeGrantRequest(
as,
Expand Down
2 changes: 2 additions & 0 deletions src/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import DiscordAuthProvider from "./oauth2/discord.js"
import FacebookAuthProvider from "./oauth2/facebook.js"
import SlackAuthProvider from "./oidc/slack.js"
import Auth0AuthProvider from "./oauth2/auth0.js"
import OktaAuthProvider from "./oidc/okta.js"
import CognitoAuthProvider from "./oidc/cognito.js"
import KeyCloakAuthProvider from "./oidc/keycloak.js"
import PasskeyAuthProvider from "./passkey.js"
Expand Down Expand Up @@ -34,4 +35,5 @@ export {
JumpCloudAuthProvider,
TwitchAuthProvider,
PasswordProvider,
OktaAuthProvider,
}
106 changes: 106 additions & 0 deletions src/providers/oidc/okta.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import type {
AccountInfo,
OIDCProviderConfig,
OAuthBaseProviderConfig,
} from "../../types.js"

interface OktaAuthConfig extends OAuthBaseProviderConfig {
domain: string
}

/**
* Add Okta OIDC Provider
*
* #### Callback or Redirect URL pattern
*
* - For Admin
* ```
* https://example.com/api/admin/oauth/callback/{name}
* ```
*
* - For App
* ```
* https://example.com/api/{app_name}/oauth/callback/{name}
* ```
*
* #### Plugin Setup
*
* ```ts
* import { Plugin } from 'payload'
* import {adminAuthPlugin, appAuthPlugin} from "payload-auth-plugin"
* import {OktaAuthProvider} from "payload-auth-plugin/providers"
*
* export const plugins: Plugins[] = [
* //For Admin
* adminAuthPlugin({
* accountsCollectionSlug: 'adminAccounts',
* providers:[
* OktaAuthProvider({g,
* domain: process.env.KEYCLOAK_DOMAIN as string,
* client_id: process.env.KEYCLOAK_CLIENT_ID as string,
* client_secret: process.env.KEYCLOAK_CLIENT_SECRET as string,
* })
* ]
* })
*
* // For App
* appAuthPlugin({
* name: 'app'
* secret: process.env.APP_AUTH_SECRET,
* accountsCollectionSlug: 'adminAccounts',
* usersCollectionSlug: 'appUsers',
* accountsCollectionSlug: 'appAccounts',
* providers:[
* OktaAuthProvider({
* domain: process.env.KEYCLOAK_DOMAIN as string,
* client_id: process.env.KEYCLOAK_CLIENT_ID as string,
* client_secret: process.env.KEYCLOAK_CLIENT_SECRET as string,
* })
* ]
* })
* ]
* ```
*
*/

function encodeString(s: string): number {
let h = 0;
const l = s.length;
let i = 0;
if (l > 0) {
while (i < l) {
h = ((h << 5) - h + s.charCodeAt(i++)) | 0; // Bitwise operations to create a hash
}
}
return h;
}


function OktaAuthProvider(config: OktaAuthConfig): OIDCProviderConfig {
const { domain, ...restConfig } = config;

const stateCode = encodeString(config.client_id).toString()

return {
...restConfig,
id: 'okta',
scope: "email openid profile",
issuer: `https://${domain}`,
name: "Okta",
algorithm: "oidc",
kind: "oauth",
params: {
state: `state-${stateCode}`
},
profile: (profile): AccountInfo => {
return {
sub: profile.sub as string,
name: profile.name as string,
email: profile.email as string,
picture: profile.picture as string,
}
},
}
}

export default OktaAuthProvider