0

In my JSS application, how do I support both authenticated (logged in) GraphQL endpoints and in-secure (public/open) GraphQL endpoints?

The example Angular application shows the usage of only one public endpoint.

0

I'm going to answer this from an Angular perspective and using the sample JSS Angular application as a point of reference. The samples below assume you're using Apollo for your queries.

JSS Configuration

Firstly in your JSS configuration we setup two endpoints.

  • One that requires a Sitecore user to be logged in.

/sitecore/api/GraphQL/defaults/security/systemService
  • One that is open and can be called by any anonymous user.

/sitecore/api/GraphQL/defaults/security/publicService

<endpoints>
 <myGraphQLEndpoint url="/api/mysecureapi" type="Sitecore.Services.GraphQL.Hosting.DatabaseAwareGraphQLEndpoint, Sitecore.Services.GraphQL.NetFxHost" resolve="true">
<url>$(url)</url>

<enabled>true</enabled>
<enableSubscriptions>true</enableSubscriptions>

<!-- lock down the endpoint when deployed to content delivery -->
<graphiql role:require="ContentDelivery">false</graphiql>
<enableSchemaExport role:require="ContentDelivery">false</enableSchemaExport>
<enableStats role:require="ContentDelivery">false</enableStats>
<enableCacheStats role:require="ContentDelivery">false</enableCacheStats>
<disableIntrospection role:require="ContentDelivery">true</disableIntrospection>

<schema hint="list:AddSchemaProvider">
  ... INSERT GRAPHQL SCHEMAS HERE
</schema>

<!-- Enables the 'jss' graph nodes that are preformatted to use with JSS rendering components, and the datasource resolving queries for JSS -->
<extenders hint="list:AddExtender">
  <layoutExtender type="Sitecore.JavaScriptServices.GraphQL.JssExtender, Sitecore.JavaScriptServices.GraphQL" resolve="true" />
</extenders>

<!-- Determines the security of the service. 'publicService' is open to anonymous access, but requires an SSC API key. -->
<security ref="/sitecore/api/GraphQL/defaults/security/systemService" />

<!-- Determines how performance is logged for the service. Defaults are defined in Sitecore.Services.GraphQL.config -->
<performance ref="/sitecore/api/GraphQL/defaults/performance/standard" />
</myGraphQLEndpoint>

<myOpenGraphQLEndpoint url="/api/myopenapi" type="Sitecore.Services.GraphQL.Hosting.DatabaseAwareGraphQLEndpoint, Sitecore.Services.GraphQL.NetFxHost" resolve="true">
<url>$(url)</url>

<enabled>true</enabled>
<enableSubscriptions>true</enableSubscriptions>

<!-- lock down the endpoint when deployed to content delivery -->
<graphiql role:require="ContentDelivery">false</graphiql>
<enableSchemaExport role:require="ContentDelivery">false</enableSchemaExport>
<enableStats role:require="ContentDelivery">false</enableStats>
<enableCacheStats role:require="ContentDelivery">false</enableCacheStats>
<disableIntrospection role:require="ContentDelivery">true</disableIntrospection>

<schema hint="list:AddSchemaProvider">
    ... INSERT GRAPHQL SCHEMAS HERE
</schema>

<!-- Determines the security of the service. 'publicService' is open to anonymous access, but requires an SSC API key. -->
<security ref="/sitecore/api/GraphQL/defaults/security/publicService" />

<performance ref="/sitecore/api/GraphQL/defaults/performance/standard" />
</myOpenGraphQLEndpoint>

Angular Code

(Based on the example jss-graphql.module.ts )

1) Inside of jss-graphql.module.ts

Introduce 2 Apollo clients:

 private readonly apollo: Apollo,          // Secure
 private readonly apolloOpenApi: Apollo,   // Anonymous, no Auth required

Both Apollo clients will be initialised once (each) within this typescript file.

Create two environment variables in your application:

  • environment.graphQLEndpoint = /api/myopenapi
  • environment.graphQLOpenEndpoint = /api/mysecureapi

Initialise the secure connection as normal:

  const batchHttp = this.httpLink.create({ uri: environment.graphQLEndpoint, withCredentials: true });

  ...

  this.apollo.create({
  link: automaticPersistHttp,
  cache: cache,
  ssrMode: isPlatformServer(this.platformId),
  ssrForceFetchDelay: 100,
});

Initialise the insecure connection with a named value for the apollo client (in this case 'Open'):

const batchHttp = this.httpLink.create({ uri: environment.graphQLOpenEndpoint, withCredentials: true });

...

this.apolloOpenApi.create({
  link: automaticPersistHttp,
  cache: new InMemoryCache(),
  ssrMode: isPlatformServer(this.platformId),
  ssrForceFetchDelay: 50,
}, 'Open');

The named client will be used to differentiate the two.

2) Inside of jss-graphql.service.ts (Based on example jss-graphql.service.ts)

In your constructor make sure that both Apollo clients get injected.

   constructor(
private readonly apollo: Apollo,
private readonly apolloOpenApi: Apollo,
private readonly sitecoreContext: JssContextService,
@Inject(PLATFORM_ID) private readonly platformId: string,
) {
this.isEditingOrPreviewingAndSsr =
  isPlatformServer(this.platformId) &&
  this.sitecoreContext.state.value.sitecore.context.pageState !== 'normal';
}

Introduce a new function called queryOpen that will allow you to call the non-secure apollo client.

queryOpen<T, V = R>(options: QueryOptions<V> & JssGraphQLOptions): Observable<ApolloQueryResult<T>> {

...

const observable = this.apolloOpenApi.use('Open').query<T>(options);

...

return observable;

}

All other secure queries can go via the original query for Authenticated calls inside of jss-graphql.service.ts (Based on example jss-graphql.service.ts

3) Each component that makes a query will now be able to call either queryOpen or query depending on its authentication needs.

  this.query$ = this.graphQLService.queryOpen({
    query: ComponentQuery,
    variables: data
  });

 this.query$ = this.graphQLService.query({
    query: ComponentQuery,
    variables: data
  });

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.