SaaS CMS has officially launched! Learn more now.

OpenID Connect and disconnected FE Auth for contentManagement and contentDelivery APIs

Nat
Nat
Vote:
 

as the title suggests, we are using a diconnected FE for our Epi 12 instance

as we are doing server side rendering for the client facing site itself, I am assuming we can use the client_credentials flow and simply call the api/episerver/connect/token endpoint with the client_id and client_secret parameters to retreive the access_token, as set in the startup (below) and it does appear to work - certainly a token is returned - the client app is setup as below

      services.AddOpenIDConnect<ApplicationUser>(
          useDevelopmentCertificate: _webHostingEnvironment.IsDevelopment(),
          signingCertificate: null,
          encryptionCertificate: null,
          createSchema: true,
          options =>
          {
            options.Applications.Add(new OpenIDConnectApplication
            {
              ClientId = "clientId",
              ClientSecret = "SuperSecretPassword",
              Scopes = { ContentDefinitionsApiOptionsDefaults.Scope, ContentManagementApiOptionsDefaults.Scope, ContentDeliveryApiOptionsDefaults.Scope },
            });
          });

although it does not seem to require this token to be used for a simple contentApiRequest, unless I set 

      services.AddContentManagementApi(OpenIDConnectOptionsDefaults.AuthenticationScheme, options =>
      {
        options.DisableScopeValidation = true;
      });

but then calling /api/episerver/v3.0/content/7  gives a 403 forbidden even when the token in attached to the request. Ideally we would like to lock down the content api so only authorized clients can access it. I have also gone into the Access Rights part of the admin pages, and ensured that the client app has all the permissions in there - but this seems to make no difference at all.
likewise when trying to make a ContentManagementApi call with the same token to api/episerver/v3.0/contentmanagement/7 we get a 401 unauthorized. Seems like I am missing a step somewhere ?!

as to calling the api from within the editor - so being able to view unpublished content and things like that, respecting the logged in users permissions etc. it seems like we need to use 

  • the ContentManagementApi (rather than the ContentDeliveryApi) otherwise we can’t see unpublished content
  • is the authorization code flow is the correct flow to use (I appreciate I have not added the application to the config as yet)? It’s unclear to me as to how this interacts with the UI, and whether it should in fact replace the standard log in mechanism (util/login) or something else 

as always any help much appreciated

#280579
Edited, May 19, 2022 10:07
Vote:
 

Paste the access token on jwt.io and see what it contains for leads.

Haven't checked on version 12 but I assume you can still also take control using your own ContentApiAuthorizationService and see a little bit of what's happening internally.

https://krompaco.nu/2018/12/content-delivery-api-and-custom-authorization/

#280873
May 25, 2022 15:11
Vote:
 

Following configuration does not affect the Content Delivery API:

 services.AddContentManagementApi(OpenIDConnectOptionsDefaults.AuthenticationScheme, options =>
{
      options.DisableScopeValidation = true;
});

This only disabled the scope validation in Content Management API. So seems like you have mixed these up somehow?

A 403 means the request didn't have sufficient access rights. The error message might state more information that can be helpful, but in general it means your'e missing a scope (if scope validation is enabled) or you don't have read access to the content item.

The easiest way to lock down the APIs completely from anonyous calls is to enable scope validation.

If you get a 401 it means your request is not authenticated at all, so either the token is invalid or expired, or you forgot to include the token.

If you want to enable preview of unpublished content, I recommend you using the endpoint where content is resolved by its URL, e.g. /api/episerver/v3.0/content/?contentUrl=xxx. If you then configure the application for external templates, the preview iframe will try to load the external URL instead:

services
   .AddCmsAspNetIdentity<ApplicationUser>()
   .AddCms()
   .ConfigureForExternalTemplates()
   .Configure<ExternalApplicationOptions>(options => options.OptimizeForDelivery = true)

In admin UI you can define what the external URL is for your application by selecting 'Primary' as host type and 'Edit' on the host for the backend.

Authorization code flow is the correct flow if you're authenticating on behalf of a user. 

#281198
Edited, Jun 01, 2022 11:19
Vote:
 
#281199
Jun 01, 2022 11:20
Nat
Vote:
 

Hi Johan

I have seen the decoupled music festival solution.
I was under the impression that when using the CMS UI to preview pages etc, that the ContentManagementApi needed to be used, but it seems that if the application cookie is sent with the request, as long as the format of the contentUrl is like so : /api/episerver/v3.0/content/?contentUrl=EPiServer/CMS/Content/en/,,5/?epieditmode=true you can get json for unpublished pages which is necessary for previewing in the CMS UI. as long as there is no scope specified in the 

services.AddContentDeliveryApi();

line, but then the api is completely open.

but if I change the startup to 

services.AddContentDeliveryApi(OpenIDConnectOptionsDefaults.AuthenticationScheme);

I can request a token with the credentials set in the

services.AddOpenIDConnect<ApplicationUser>(options =>
{
options.Applications.Add(
  new OpenIDConnectApplication { 
    ClientId = "clientId", 
    ClientSecret = "SuperSecretPassword", 
    Scopes = { ContentDefinitionsApiOptionsDefaults.Scope, ContentDeliveryApiOptionsDefaults.Scope } 
});

but the cookie is no longer sufficient to make the call that worked before.

is there no way to have both of these working at once?

we do not have users logging into the published site itself, which is where the Auth flow would work, but will require authenticated users (apps really) to be accessing the ContentApi outside of the CMS itself

#281434
Edited, Jun 06, 2022 8:51
Vote:
 

Cookie authentication only works on the same domain, i.e. not cross-origin. If you're hosting the app on the same domain as the CMS, then "it just works" and cookie authentication can be used, see https://github.com/episerver/content-delivery-js-sdk/tree/master/samples/music-festival-vue-coupled. Otherwise you need to implement any other form of authentication, e.g. OpenID Connect and JWT/bearer tokens or a custom authentication handler. 

When you write "disconnected FE", what do you actually mean? Is the FE hosted in a separate app on a separate domain? Does it also have a backend?

If you change to services.AddContentDeliveryApi(OpenIDConnectOptionsDefaults.AuthenticationScheme);, cookie authentication can obviously not work since this scheme is only handling OpenID Connect.

You want apps to be authenticated when calling the REST API, then these apps need to have a backend too, since you cannot store the client secret in a browser. Browsers cannot hold secrets. Cookie authentication would not work either, since it most likely would require user input. It seem like you want to have app/service-to-app/service communication, then you should use the client credentials flow in OpenID Connect and authenticate you app with a client ID and secret, then include the access token as a JWT/bearer token in  the authorization header in each API call. 

#281435
Jun 06, 2022 10:40
Vote:
 

Also, services.AddContentDeliveryApi(); does not make the API completely open, access rights are still evaulated. So if you remove read access for 'Everyone', you won't be able to query for content without being authenticated. Loading unpublished content requires at least edit access.

#281436
Jun 06, 2022 10:42
Nat
Vote:
 

thanks for the detailed reply

yes our FE and BE are on different subdomains, so cookies wont work without changing Lax to None and settign the domain for the application cookie but again that wont work on multisite either as far as I can tell and probably isnt a good idea anyway.

had another look at the music-festival-decoupled and see that I can use the standard login page to handle the openid login as long as the return url is set to the authorise endpoint, with the appropriate querystring values, and it does still appear to set a cookie to recognise that the user is logged in.

however, I cant see a reliable way - as we will need multisite - to get the redirecturls to cover all the multisite domains without adding each domain in that redirecturi list in the openid application setup in startup.cs - which seems to sort of kill multisite a bit. not sure if there is a cunning way to deal with that as certanily dont want to have to re-deploy and add to that list each time we add a new site.

likewise, is the simplest way to get the correct items in the querystring for logging in, to maybe create a simple login controller which would redirect to the standard util/login to handle that?

so mysite.com/login -> redirects to util/login?returnurl=/api/episerver/connect/authorize?client_id=etc...

#281890
Edited, Jun 15, 2022 8:15
Vote:
 

There are APIs available to manage applications, so you can subscribe to events when sites are updated (I believe there these types of events) and then update the redirect URIs in the application(s).  Usually you have to do a deploy anyway when settings up a new website, maybe you have updated templates and/or new content types.

If you set up the frontend to use the CMS as an identity provider, then the redirection would work automatically, just like in the example. But as I understood, you don't want the end users to log in? The application itself should authenticate. Then there is no login page or redirections involved. You "just" acquire an access token and include it in every request.

#281900
Jun 15, 2022 13:16
Nat
Vote:
 

hi

  1. end users (website visitors) just view the site - dont log in
  2. cms editors need to log in, but the cookie is no good as the FE is on a subdomain - so to "just" acquire the token, I figured I could use the auth flow with the util/login page - difference I guess (compared to the music festival example) is the login for them is not triggered by the FE at all so not sure how to trigger that. maybe using the Azure AD for the identity provider would be better. I didnt think this was such an edge case really.
  3. FE for 'Live' site uses SSR so will also need a token, presumably via the client_credentials flow

maybe we should jsut set up some reverse proxy rules to serve the sites from the same domain (no subdomains)

#281903
Jun 15, 2022 14:50
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.