Skip to content

Commit

Permalink
Merge pull request #2389 from Infisical/misc/support-glob-patterns-oidc
Browse files Browse the repository at this point in the history
misc: support glob patterns for OIDC
  • Loading branch information
maidul98 authored Sep 9, 2024
2 parents 2b39d9e + 8e68d21 commit 45f3675
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import picomatch from "picomatch";

export const doesFieldValueMatchOidcPolicy = (fieldValue: string, policyValue: string) =>
policyValue === fieldValue || picomatch.isMatch(fieldValue, policyValue);
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identit
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
import { TOrgBotDALFactory } from "../org/org-bot-dal";
import { TIdentityOidcAuthDALFactory } from "./identity-oidc-auth-dal";
import { doesFieldValueMatchOidcPolicy } from "./identity-oidc-auth-fns";
import {
TAttachOidcAuthDTO,
TGetOidcAuthDTO,
Expand Down Expand Up @@ -123,15 +124,19 @@ export const identityOidcAuthServiceFactory = ({
}) as Record<string, string>;

if (identityOidcAuth.boundSubject) {
if (tokenData.sub !== identityOidcAuth.boundSubject) {
if (!doesFieldValueMatchOidcPolicy(tokenData.sub, identityOidcAuth.boundSubject)) {
throw new ForbiddenRequestError({
message: "Access denied: OIDC subject not allowed."
});
}
}

if (identityOidcAuth.boundAudiences) {
if (!identityOidcAuth.boundAudiences.split(", ").includes(tokenData.aud)) {
if (
!identityOidcAuth.boundAudiences
.split(", ")
.some((policyValue) => doesFieldValueMatchOidcPolicy(tokenData.aud, policyValue))
) {
throw new ForbiddenRequestError({
message: "Access denied: OIDC audience not allowed."
});
Expand All @@ -142,7 +147,9 @@ export const identityOidcAuthServiceFactory = ({
Object.keys(identityOidcAuth.boundClaims).forEach((claimKey) => {
const claimValue = (identityOidcAuth.boundClaims as Record<string, string>)[claimKey];
// handle both single and multi-valued claims
if (!claimValue.split(", ").some((claimEntry) => tokenData[claimKey] === claimEntry)) {
if (
!claimValue.split(", ").some((claimEntry) => doesFieldValueMatchOidcPolicy(tokenData[claimKey], claimEntry))
) {
throw new ForbiddenRequestError({
message: "Access denied: OIDC claim not allowed."
});
Expand Down
5 changes: 5 additions & 0 deletions docs/documentation/platform/identities/oidc-auth/general.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,12 @@ In the following steps, we explore how to create and use identities to access th
- Access Token Max TTL (default is `2592000` equivalent to 30 days): The maximum lifetime for an acccess token in seconds. This value will be referenced at renewal time.
- Access Token Max Number of Uses (default is `0`): The maximum number of times that an access token can be used; a value of `0` implies infinite number of uses.
- Access Token Trusted IPs: The IPs or CIDR ranges that access tokens can be used from. By default, each token is given the `0.0.0.0/0`, allowing usage from any network address.
<Info>
The `subject`, `audiences`, and `claims` fields support glob pattern matching; however, we highly recommend using hardcoded values whenever possible.
</Info>

</Step>

<Step title="Adding an identity to a project">
To enable the identity to access project-level resources such as secrets within a specific project, you should add it to that project.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ In the following steps, we explore how to create and use identities to access th
- Access Token Max TTL (default is `2592000` equivalent to 30 days): The maximum lifetime for an acccess token in seconds. This value will be referenced at renewal time.
- Access Token Max Number of Uses (default is `0`): The maximum number of times that an access token can be used; a value of `0` implies infinite number of uses.
- Access Token Trusted IPs: The IPs or CIDR ranges that access tokens can be used from. By default, each token is given the `0.0.0.0/0`, allowing usage from any network address.

<Tip>If you are unsure about what to configure for the subject, audience, and claims fields you can use [github/actions-oidc-debugger](https://github.com/github/actions-oidc-debugger) to get the appropriate values. Alternatively, you can fetch the JWT from the workflow and inspect the fields manually.</Tip>
<Info>The `subject`, `audiences`, and `claims` fields support glob pattern matching; however, we highly recommend using hardcoded values whenever possible.</Info>
</Step>
<Step title="Adding an identity to a project">
To enable the identity to access project-level resources such as secrets within a specific project, you should add it to that project.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { useEffect } from "react";
import { Controller, useFieldArray, useForm } from "react-hook-form";
import { faQuestionCircle } from "@fortawesome/free-regular-svg-icons";
import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";

import { createNotification } from "@app/components/notifications";
import { Button, FormControl, IconButton, Input, TextArea } from "@app/components/v2";
import { Button, FormControl, IconButton, Input, TextArea, Tooltip } from "@app/components/v2";
import { useOrganization, useSubscription } from "@app/context";
import { useAddIdentityOidcAuth, useUpdateIdentityOidcAuth } from "@app/hooks/api";
import { IdentityAuthMethod } from "@app/hooks/api/identities";
Expand Down Expand Up @@ -258,7 +259,19 @@ export const IdentityOidcAuthForm = ({
control={control}
name="boundSubject"
render={({ field, fieldState: { error } }) => (
<FormControl label="Subject" isError={Boolean(error)} errorText={error?.message}>
<FormControl
label="Subject"
isError={Boolean(error)}
errorText={error?.message}
icon={
<Tooltip
className="text-center"
content={<span>This field supports glob patterns</span>}
>
<FontAwesomeIcon icon={faQuestionCircle} size="sm" />
</Tooltip>
}
>
<Input {...field} type="text" />
</FormControl>
)}
Expand All @@ -267,7 +280,19 @@ export const IdentityOidcAuthForm = ({
control={control}
name="boundAudiences"
render={({ field, fieldState: { error } }) => (
<FormControl label="Audiences" isError={Boolean(error)} errorText={error?.message}>
<FormControl
label="Audiences"
isError={Boolean(error)}
errorText={error?.message}
icon={
<Tooltip
className="text-center"
content={<span>This field supports glob patterns</span>}
>
<FontAwesomeIcon icon={faQuestionCircle} size="sm" />
</Tooltip>
}
>
<Input {...field} type="text" placeholder="service1, service2" />
</FormControl>
)}
Expand All @@ -282,6 +307,16 @@ export const IdentityOidcAuthForm = ({
<FormControl
className="mb-0 flex-grow"
label={index === 0 ? "Claims" : undefined}
icon={
index === 0 ? (
<Tooltip
className="text-center"
content={<span>This field supports glob patterns</span>}
>
<FontAwesomeIcon icon={faQuestionCircle} size="sm" />
</Tooltip>
) : undefined
}
isError={Boolean(error)}
errorText={error?.message}
>
Expand Down

0 comments on commit 45f3675

Please sign in to comment.