Skip to content

Commit

Permalink
Merge branch 'main' into patch-1
Browse files Browse the repository at this point in the history
  • Loading branch information
jtoar authored Jun 26, 2022
2 parents 1d90018 + 47734f4 commit a50bbb7
Show file tree
Hide file tree
Showing 18 changed files with 502 additions and 378 deletions.
45 changes: 45 additions & 0 deletions docs/docs/auth/azure.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ sidebar_label: Azure

# Azure Active Directory Authentication

+++ View Installation and Setup

> **Azure AD B2C Compatibility Note**
>
> Microsoft [Azure AD B2C](https://docs.microsoft.com/en-us/azure/active-directory-b2c/overview) auth product *is* compatible with this auth provider. See below for additional configuration details.
## Installation

The following CLI command will install required packages and generate boilerplate code and files for Redwood Projects:
Expand Down Expand Up @@ -95,3 +101,42 @@ await getToken({
```

See [acquireTokenSilent](https://azuread.github.io/microsoft-authentication-library-for-js/ref/classes/_azure_msal_browser.publicclientapplication.html#acquiretokensilent), [Resources and Scopes](https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/resources-and-scopes.md#resources-and-scopes) or [full class documentation](https://pub.dev/documentation/msal_js/latest/msal_js/PublicClientApplication-class.html#constructors) for more documentation.

## Azure AD B2C specific configuration

Using Azure AD B2C with [hosted user flows](https://docs.microsoft.com/en-us/azure/active-directory-b2c/add-sign-up-and-sign-in-policy?pivots=b2c-user-flow) requires 2 extra settings

#### Update the .env file:

- [MS Documentation about B2C JWT Issuer](https://docs.microsoft.com/en-us/azure/active-directory-b2c/tokens-overview)

- [MS Documentation about MSAL, Azure B2C (authority|known authorities) parameters](https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/working-with-b2c.md
)

``` bash title="./.env"

AZURE_ACTIVE_DIRECTORY_AUTHORITY=https://{your-microsoft-tenant-name}.b2clogin.com/{{your-microsoft-tenant-name}}.onmicrosoft.com/{{your-microsoft-user-flow-id}}

AZURE_ACTIVE_DIRECTORY_JWT_ISSUER=https://{{your_microsoft_tenant_name}}.b2clogin.com/{{your_microsoft_tenant_id}}/v2.0/

AZURE_ACTIVE_DIRECTORY_KNOWN_AUTHORITY=https://{{ms_tenant_name}}.b2clogin.com

```

#### Update const activeDirectoryClient instance
This lets the MSAL (Microsoft Authenication Library) web side client know about our new B2C allowed authority that we defined in the .env file
``` jsx title="./web/App.jsx|.tsx

const azureActiveDirectoryClient = new PublicClientApplication({
auth: {
clientId: process.env.AZURE_ACTIVE_DIRECTORY_CLIENT_ID,
authority: process.env.AZURE_ACTIVE_DIRECTORY_AUTHORITY,
redirectUri: process.env.AZURE_ACTIVE_DIRECTORY_REDIRECT_URI,
postLogoutRedirectUri: process.env.AZURE_ACTIVE_DIRECTORY_LOGOUT_REDIRECT_URI,
// highlight-next-line
knownAuthorities:[process.env.AZURE_ACTIVE_DIRECTORY_KNOWN_AUTHORITY]
},
})
```

Now you can call the login and logout functions from useAuth(), and everything should just work®
4 changes: 2 additions & 2 deletions docs/docs/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import { useAuth } from '@redwoodjs/auth'

export const MyComponent = () => {
const { currentUser, isAuthenticated, logIn, logOut } = useAuth()

return (
<ul>
<li>The current user is: {currentUser}</li>
Expand Down Expand Up @@ -79,7 +79,7 @@ export const MyComponent = () => {
{hasRole('admin') && (
<Link to={routes.admin()}>Admin</Link>
)}

{hasRole(['author', 'editor']) && (
<Link to={routes.posts()}>Admin</Link>
)}
Expand Down
21 changes: 18 additions & 3 deletions docs/docs/services.md
Original file line number Diff line number Diff line change
Expand Up @@ -645,14 +645,14 @@ So `validateUniqueness()` first tries to find a record with the given fields, an
> **Why use this when the database can verify uniqueness with a UNIQUE INDEX database constraint?**
>
> You may be in a situation where you can't have a unique index (supporting a legacy schema, perhaps), but still want to make sure the data is unique before proceeding. There is also the belief that you shouldn't have to count on the database to validate your data—that's a core concern of your business logic, and your business logic should live in your Services in a Redwood app.
>
> Another issue is that the error raised by Prisma when a record validates a unique index is swallowed by GraphQL and so you can't report it to the user (there are still ways around this, but it involves catching and re-throwing a different error). The error raised by `validateUniqueness()` is already safe-listed and allowed to be sent to the browser.
>
> Another issue is that the error raised by Prisma when a record validates a unique index is swallowed by GraphQL and so you can't report it to the user (there are still ways around this, but it involves catching and re-throwing a different error). The error raised by `validateUniqueness()` is already safe-listed and allowed to be sent to the browser.
#### Arguments
1. The name of the db table accessor that will be checked (what you would call on `db` in a normal Prisma call). If you'd call `db.user` then this value is `"user"`.
2. An object, containing the db fields/values to check for uniqueness, like `{ email: '[email protected]' }`. Can also include additional options explained below that provide for a narrower scope for uniqueness requirements, and a way for the record to identify itself and not create a false positive for an existing record.
3. [Optional] An object with options.
3. [Optional] An object with options. `message` - custom error message. `db` - custom instance of the PrismaClient to use
4. Callback to be invoked if record is found to be unique.
In its most basic usage, say you want to make sure that a user's email address is unique before creating the record. `input` is an object containing all the user fields to save to the database, including `email` which must be unique:
Expand All @@ -677,6 +677,21 @@ const createUser = (input) => {
}
```
You can provide the PrismaClient to be used for the transaction and callback.
```jsx
import { db } from 'src/lib/db'

const createUser = (input) => {
return validateUniqueness('user',
{ email: input.email },
{ db },
(db) => db.user.create({ data: input })
)
}
```
> If you are overwriting the DATABASE_URL in your `src/lib/db` instantiation of the PrismaClient, you need to use this option. If not provided, a vanilla `new PrismaClient()` is used to run the callback that will not respect any custom configurations not represented in your `prisma.schema`
Be sure that both your callback and the surrounding `validateUniqueness()` function are `return`ed or else your service function will have nothing to return to its consumers, like GraphQL.
##### $self
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
"core-js": "3.23.2",
"cypress": "9.7.0",
"cypress-wait-until": "1.7.2",
"eslint": "8.16.0",
"eslint": "8.18.0",
"fast-glob": "3.2.11",
"fs-extra": "10.1.0",
"is-port-reachable": "3.1.0",
Expand Down
10 changes: 8 additions & 2 deletions packages/api/src/auth/decoders/azureActiveDirectory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ export const azureActiveDirectory = async (
token: string
): Promise<null | Record<string, unknown>> => {
return new Promise((resolve, reject) => {
const { AZURE_ACTIVE_DIRECTORY_AUTHORITY } = process.env
const {
AZURE_ACTIVE_DIRECTORY_AUTHORITY,
AZURE_ACTIVE_DIRECTORY_JWT_ISSUER,
} = process.env

// Make sure we have required environment variables
if (!AZURE_ACTIVE_DIRECTORY_AUTHORITY) {
Expand Down Expand Up @@ -43,7 +46,10 @@ export const azureActiveDirectory = async (
})
},
{
issuer: `${AZURE_ACTIVE_DIRECTORY_AUTHORITY}/v2.0`,
//Set via .env variable (Azure AD B2C use case) or assumes using normal AZURE AD issuer
issuer: AZURE_ACTIVE_DIRECTORY_JWT_ISSUER
? AZURE_ACTIVE_DIRECTORY_JWT_ISSUER
: `${AZURE_ACTIVE_DIRECTORY_AUTHORITY}/v2.0`,
algorithms: ['RS256'],
},
(verifyError, decoded) => {
Expand Down
30 changes: 30 additions & 0 deletions packages/api/src/validations/__tests__/validations.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1144,4 +1144,34 @@ describe('validateUniqueness', () => {
}
expect.assertions(1)
})

it('uses the given prisma client', async () => {
const mockFindFirstOther = jest.fn()
mockFindFirstOther.mockImplementation(() => ({
id: 2,
email: '[email protected]',
}))
const mockPrismaClient = {
$transaction: async (func) =>
func({
user: {
findFirst: mockFindFirstOther,
},
}),
}

expect(mockFindFirstOther).not.toBeCalled()

await expect(
validateUniqueness(
'user',
{ email: '[email protected]' },
{ db: mockPrismaClient },
() => {}
)
).rejects.toThrowError('email must be unique')

expect(mockFindFirstOther).toBeCalled()
expect(mockFindFirst).not.toBeCalled()
})
})
28 changes: 25 additions & 3 deletions packages/api/src/validations/validations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,9 @@ interface PresenceValidatorOptions extends WithOptionalMessage {
allowEmptyString?: boolean
}

interface UniquenessValidatorOptions extends WithRequiredMessage {}
interface UniquenessValidatorOptions extends WithOptionalMessage {
db?: PrismaClient
}
type UniquenessWhere = Record<'AND' | 'NOT', Array<Record<string, unknown>>>

interface ValidationRecipe {
Expand Down Expand Up @@ -612,6 +614,18 @@ export const validateWith = (func: () => void) => {
// }, (db) => {
// return db.create(data: { email })
// })
//
// const myCustomDb = new PrismaClient({
// log: emitLogLevels(['info', 'warn', 'error']),
// datasources: {
// db: {
// url: process.env.DATABASE_URL,
// },
// },
// })
// return validateUniqueness('user', { email: 'rob@redwoodjs.com' }, { prismaClient: myCustomDb}, (db) => {
// return db.create(data: { email })
// })
export async function validateUniqueness(
model: string,
fields: Record<string, unknown>,
Expand All @@ -632,10 +646,10 @@ export async function validateUniqueness(
| ((tx: PrismaClient) => Promise<any>),
callback?: (tx: PrismaClient) => Promise<any>
): Promise<any> {
const db = new PrismaClient()
const { $self, $scope, ...rest } = fields
let options = {}
let options: UniquenessValidatorOptions = {}
let validCallback: (tx: PrismaClient) => Promise<any>
let db = null

if (typeof optionsOrCallback === 'function') {
validCallback = optionsOrCallback
Expand All @@ -644,6 +658,14 @@ export async function validateUniqueness(
validCallback = callback as (tx: PrismaClient) => Promise<any>
}

if (options.db) {
const { db: customDb, ...restOptions } = options
options = restOptions
db = customDb
} else {
db = new PrismaClient()
}

const where: UniquenessWhere = {
AND: [rest],
NOT: [],
Expand Down
2 changes: 1 addition & 1 deletion packages/auth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"@supabase/supabase-js": "1.35.3",
"@types/netlify-identity-widget": "1.9.3",
"@types/react": "17.0.45",
"firebase": "9.8.2",
"firebase": "9.8.3",
"firebase-admin": "10.2.0",
"gotrue-js": "0.9.29",
"jest": "27.5.1",
Expand Down
37 changes: 18 additions & 19 deletions packages/auth/src/authClients/azureActiveDirectory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,29 +48,28 @@ export const azureActiveDirectory = (
// As we are using the redirect flow, we need to call and wait for handleRedirectPromise to complete.
// This should only happen on a valid redirect, and having it in the restoreAuthState makes sense for now.
// https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/v1-migration.md#3-update-your-code
if (window.location.href.includes('#code=')) {
// Wait for promise
await client.handleRedirectPromise()
await client.handleRedirectPromise().then((token) => {
if (token) {
// Get accounts
const accounts = client.getAllAccounts()

// Get accounts
const accounts = client.getAllAccounts()
switch (accounts.length) {
case 0:
// No accounts so we need to login
client.loginRedirect()
break

switch (accounts.length) {
case 0:
// No accounts so we need to login
client.loginRedirect()
break
case 1:
// We have one account so we can set it as active
client.setActiveAccount(accounts[0])
break

case 1:
// We have one account so we can set it as active
client.setActiveAccount(accounts[0])
break

default:
// We most likely have multiple accounts so we need to ask the user which one to use
client.loginRedirect()
default:
// We most likely have multiple accounts so we need to ask the user which one to use
client.loginRedirect()
}
}
}
})
},
}
}
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"param-case": "3.0.4",
"pascalcase": "1.0.0",
"pluralize": "8.0.0",
"prettier": "2.6.2",
"prettier": "2.7.1",
"prisma": "3.15.2",
"prompts": "2.4.2",
"rimraf": "3.0.2",
Expand Down
2 changes: 1 addition & 1 deletion packages/codemods/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"findup-sync": "5.0.0",
"jest": "27.5.1",
"jscodeshift": "0.13.1",
"prettier": "2.6.2",
"prettier": "2.7.1",
"tasuku": "2.0.0",
"toml": "3.0.0",
"yargs": "17.5.1"
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
"css-loader": "6.7.1",
"css-minimizer-webpack-plugin": "4.0.0",
"dotenv-webpack": "7.1.0",
"esbuild": "0.14.43",
"esbuild": "0.14.47",
"esbuild-loader": "2.19.0",
"fast-glob": "3.2.11",
"file-loader": "6.2.0",
Expand Down
4 changes: 2 additions & 2 deletions packages/eslint-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"@redwoodjs/internal": "2.0.0",
"@typescript-eslint/eslint-plugin": "5.26.0",
"@typescript-eslint/parser": "5.26.0",
"eslint": "8.16.0",
"eslint": "8.18.0",
"eslint-config-prettier": "8.5.0",
"eslint-import-resolver-babel-module": "5.3.1",
"eslint-plugin-babel": "5.3.1",
Expand All @@ -28,7 +28,7 @@
"eslint-plugin-prettier": "4.0.0",
"eslint-plugin-react": "7.30.0",
"eslint-plugin-react-hooks": "4.5.0",
"prettier": "2.6.2"
"prettier": "2.7.1"
},
"devDependencies": {
"@babel/cli": "7.16.7",
Expand Down
6 changes: 3 additions & 3 deletions packages/internal/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,16 @@
"chalk": "4.1.2",
"core-js": "3.23.2",
"deepmerge": "4.2.2",
"esbuild": "0.14.43",
"esbuild": "0.14.47",
"fast-glob": "3.2.11",
"findup-sync": "5.0.0",
"fs-extra": "10.1.0",
"graphql": "16.5.0",
"kill-port": "1.6.1",
"prettier": "2.6.2",
"prettier": "2.7.1",
"rimraf": "3.0.2",
"string-env-interpolation": "1.0.1",
"systeminformation": "5.11.16",
"systeminformation": "5.11.21",
"terminal-link": "2.1.1",
"toml": "3.0.0"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/record/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"@babel/cli": "7.16.7",
"@babel/core": "7.16.7",
"@prisma/sdk": "3.15.2",
"esbuild": "0.14.43",
"esbuild": "0.14.47",
"jest": "27.5.1"
},
"gitHead": "3905ed045508b861b495f8d5630d76c7a157d8f1"
Expand Down
4 changes: 2 additions & 2 deletions packages/telemetry/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@
"@babel/runtime-corejs3": "7.16.7",
"@redwoodjs/internal": "2.0.0",
"@redwoodjs/structure": "2.0.0",
"ci-info": "3.3.1",
"ci-info": "3.3.2",
"core-js": "3.23.2",
"cross-undici-fetch": "0.1.27",
"envinfo": "7.8.1",
"systeminformation": "5.11.16",
"systeminformation": "5.11.21",
"uuid": "8.3.2",
"yargs": "17.5.1"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"test:watch": "yarn test --watch"
},
"dependencies": {
"@apollo/client": "3.6.8",
"@apollo/client": "3.6.9",
"@babel/runtime-corejs3": "7.16.7",
"@redwoodjs/auth": "2.0.0",
"core-js": "3.23.2",
Expand Down
Loading

0 comments on commit a50bbb7

Please sign in to comment.