Skip to content
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

[NextJs] Sitecore Experience Edge Authentication #636

Merged
merged 8 commits into from
Mar 29, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ This guide will demonstrate deploying the Next.js sample app that's generated by
1. Execute the steps provided in the Next.js guide for [Getting Started](https://nextjs.org/docs/deployment#getting-started) with deployment.
2. Add [environment variables](https://nextjs.org/docs/basic-features/environment-variables#environment-variables-on-vercel) to Vercel or modify `.env` file. We recommend to use environment variables in Vercel:
* `PUBLIC_URL` - your Vercel deployment URL.
* `SITECORE_API_KEY` - your Sitecore API key is needed to build the app.
* `SITECORE_API_HOST` - your Sitecore API hostname.
* `SITECORE_API_KEY` - your Sitecore API key. For Sitecore XM, the `SITECORE_API_KEY` will be your Sitecore SSC API key. For Sitecore Experience Edge, this will be the API key provisioned here.
ambrauer marked this conversation as resolved.
Show resolved Hide resolved
* `SITECORE_API_HOST` - your Sitecore API hostname. The `SITECORE_API_HOST` is not required for Sitecore Experience Edge.
* `JSS_EDITING_SECRET` - your secret token. The `JSS_EDITING_SECRET` is optional for deployments but necessary if you want to use the Experience Editor with your Next.js Vercel deployment. Read about [connecting your Next.js application to the Experience Editor](/docs/nextjs/experience-editor/walkthrough).
* `GRAPH_QL_ENDPOINT` - your GraphQL endpoint. The `GRAPH_QL_ENDPOINT` is required for Sitecore Experience Edge. For Sitecore XM, this is typically optional. By default, the endpoint is calculated using the resolved Sitecore API hostname + the `graphQLEndpointPath` defined in your `package.json`.
3. Push the changes to your Git provider.

## `publish:end` webhook invocation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@ import { GraphQLSitemapService } from './graphql-sitemap-service';

describe('GraphQLSitemapService', () => {
const ROOT_ITEM = '/sitecore/next/home';
const graphQLLayoutService = new GraphQLSitemapService({
const graphQLSitemapService = new GraphQLSitemapService({
endpoint: 'http://jssnextweb/graphql',
apiKey: '0FBFF61E-267A-43E3-9252-B77E71CEE4BA',
});

const mockRootItemIdRequest = () => {
nock('http://jssnextweb')
nock('http://jssnextweb', {
reqheaders: {
sc_apikey: '0FBFF61E-267A-43E3-9252-B77E71CEE4BA',
},
})
.post('/graphql')
.reply(200, {
data: {
Expand All @@ -21,7 +26,11 @@ describe('GraphQLSitemapService', () => {
};

const mockPathsRequest = (results: { url: { path: string } }[]) => {
nock('http://jssnextweb')
nock('http://jssnextweb', {
reqheaders: {
sc_apikey: '0FBFF61E-267A-43E3-9252-B77E71CEE4BA',
},
})
.post('/graphql')
.reply(200, {
data: {
Expand Down Expand Up @@ -55,7 +64,7 @@ describe('GraphQLSitemapService', () => {
},
]);

const sitemap = await graphQLLayoutService.fetchSSGSitemap(['ua'], ROOT_ITEM);
const sitemap = await graphQLSitemapService.fetchSSGSitemap(['ua'], ROOT_ITEM);

expect(sitemap).to.deep.equal([
{
Expand Down Expand Up @@ -118,7 +127,7 @@ describe('GraphQLSitemapService', () => {
},
]);

const sitemap = await graphQLLayoutService.fetchSSGSitemap(['ua', 'da-DK'], ROOT_ITEM);
const sitemap = await graphQLSitemapService.fetchSSGSitemap(['ua', 'da-DK'], ROOT_ITEM);

expect(sitemap).to.deep.equal([
{
Expand Down Expand Up @@ -177,15 +186,15 @@ describe('GraphQLSitemapService', () => {

mockPathsRequest([]);

const sitemap = await graphQLLayoutService.fetchSSGSitemap(['ua'], ROOT_ITEM);
const sitemap = await graphQLSitemapService.fetchSSGSitemap(['ua'], ROOT_ITEM);

expect(sitemap).to.deep.equal([]);
});

it('should fetch sitemap if locales are not provided', async () => {
mockRootItemIdRequest();

const sitemap = await graphQLLayoutService.fetchSSGSitemap([], ROOT_ITEM);
const sitemap = await graphQLSitemapService.fetchSSGSitemap([], ROOT_ITEM);

expect(sitemap).to.deep.equal([]);
});
Expand All @@ -197,7 +206,7 @@ describe('GraphQLSitemapService', () => {
data: null,
});

const sitemap = await graphQLLayoutService.fetchSSGSitemap([], ROOT_ITEM);
const sitemap = await graphQLSitemapService.fetchSSGSitemap([], ROOT_ITEM);

expect(sitemap).to.deep.equal([]);
});
Expand All @@ -209,7 +218,7 @@ describe('GraphQLSitemapService', () => {
error: 'whoops',
});

const sitemap = await graphQLLayoutService.fetchSSGSitemap(['ua'], ROOT_ITEM);
const sitemap = await graphQLSitemapService.fetchSSGSitemap(['ua'], ROOT_ITEM);

expect(sitemap).to.deep.equal([]);
});
Expand All @@ -221,7 +230,7 @@ describe('GraphQLSitemapService', () => {
.post('/graphql')
.reply(401, 'whoops');

const sitemap = await graphQLLayoutService.fetchSSGSitemap(['ua'], ROOT_ITEM);
const sitemap = await graphQLSitemapService.fetchSSGSitemap(['ua'], ROOT_ITEM);

expect(sitemap).to.deep.equal([]);
});
Expand All @@ -246,7 +255,7 @@ describe('GraphQLSitemapService', () => {
},
]);

const sitemap = await graphQLLayoutService.fetchExportSitemap('ua', ROOT_ITEM);
const sitemap = await graphQLSitemapService.fetchExportSitemap('ua', ROOT_ITEM);

expect(sitemap).to.deep.equal([
{
Expand Down Expand Up @@ -277,7 +286,7 @@ describe('GraphQLSitemapService', () => {

mockPathsRequest([]);

const sitemap = await graphQLLayoutService.fetchExportSitemap('ua', ROOT_ITEM);
const sitemap = await graphQLSitemapService.fetchExportSitemap('ua', ROOT_ITEM);

expect(sitemap).to.deep.equal([]);
});
Expand All @@ -289,7 +298,7 @@ describe('GraphQLSitemapService', () => {
data: null,
});

const sitemap = await graphQLLayoutService.fetchSSGSitemap([], ROOT_ITEM);
const sitemap = await graphQLSitemapService.fetchSSGSitemap([], ROOT_ITEM);

expect(sitemap).to.deep.equal([]);
});
Expand All @@ -301,7 +310,7 @@ describe('GraphQLSitemapService', () => {
error: 'whoops',
});

const sitemap = await graphQLLayoutService.fetchSSGSitemap(['ua'], ROOT_ITEM);
const sitemap = await graphQLSitemapService.fetchSSGSitemap(['ua'], ROOT_ITEM);

expect(sitemap).to.deep.equal([]);
});
Expand All @@ -313,7 +322,7 @@ describe('GraphQLSitemapService', () => {
.post('/graphql')
.reply(401, 'whoops');

const sitemap = await graphQLLayoutService.fetchSSGSitemap(['ua'], ROOT_ITEM);
const sitemap = await graphQLSitemapService.fetchSSGSitemap(['ua'], ROOT_ITEM);

expect(sitemap).to.deep.equal([]);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ export type GraphQLSitemapServiceConfig = {
* Your Graphql endpoint
*/
endpoint: string;
/**
* The API key to use for authentication.
*/
apiKey: string;
};

type SearchResult = {
Expand Down Expand Up @@ -175,9 +179,9 @@ export class GraphQLSitemapService {
* Returns new graphql client instance
*/
private createClient(): GraphQLRequestClient {
const { endpoint } = this.config;
const { endpoint, apiKey } = this.config;

return new GraphQLRequestClient(endpoint);
return new GraphQLRequestClient(endpoint, apiKey);
}

/**
Expand Down
31 changes: 22 additions & 9 deletions packages/sitecore-jss/src/graphql-request-client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,40 @@ import nock from 'nock';
import { GraphQLRequestClient } from './graphql-request-client';

describe('GraphQLRequestClient', () => {
const ENDPOINT = 'http://jssnextweb/graphql';
ambrauer marked this conversation as resolved.
Show resolved Hide resolved
const endpoint = 'http://jssnextweb/graphql';

const graphQLClient = new GraphQLRequestClient(ENDPOINT);
afterEach(nock.cleanAll);

const mockGraphQLRequest = () => {
it('should execute graphql request', async () => {
nock('http://jssnextweb')
.post('/graphql')
.reply(200, {
data: {
result: 'Hello world...',
},
});
};

afterEach(nock.cleanAll);

it('should execute graphql request', async () => {
mockGraphQLRequest();

const graphQLClient = new GraphQLRequestClient(endpoint);
const data = await graphQLClient.request('test');

expect(data).to.deep.equal({ result: 'Hello world...' });
});

it('should send sc_apikey header', async () => {
anastasiya29 marked this conversation as resolved.
Show resolved Hide resolved
ambrauer marked this conversation as resolved.
Show resolved Hide resolved
const apiKey = 'cjhNRWNVOHRFTklwUjhYa0RSTnBhSStIam1mNE1KN1pyeW13c3FnRVExTT18bXRzdC1kLTAxOQ==';
nock('http://jssnextweb', {
reqheaders: {
sc_apikey: apiKey,
},
})
.post('/graphql')
.reply(200, {
data: {
result: 'Hello world...',
},
});

const graphQLClient = new GraphQLRequestClient(endpoint, apiKey);
await graphQLClient.request('test');
});
});
12 changes: 8 additions & 4 deletions packages/sitecore-jss/src/graphql-request-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,25 @@ import chalk from 'chalk';
export class GraphQLRequestClient {
/**
* Provides ability to execute graphql query using given `endpoint`
* @param {string} endpoint Your Graphql endpoint
* @param {string} endpoint The Graphql endpoint
* @param {string} [apiKey] The API key to use for authentication. This will be added as an 'sc_apikey' header.
ambrauer marked this conversation as resolved.
Show resolved Hide resolved
*/
constructor(private endpoint: string) {}
constructor(private endpoint: string, private apiKey?: string) {}

/**
* Execute graphql request
* @param {string} query graphql query
* @param {Object} variables graphql variables
*/
async request<T>(query: string, variables?: { [key: string]: unknown }): Promise<T> {
const client = new GraphQLClient(this.endpoint);
const client = new GraphQLClient(
this.endpoint,
this.apiKey ? { headers: { sc_apikey: this.apiKey } } : undefined
);
const onError = (error: unknown) => {
console.error(
chalk.red(`
Error occurred while fetching attempting to fetch graphQL data.
Error occurred while attempting to fetch graphQL data.
Endpoint: ${this.endpoint}
Query: ${query}
Variables: ${JSON.stringify(variables, null, 2)}
Expand Down
Loading