• Internal authority URL with OpenID Connect in docker for ASP.NET Core

    When setting up OpenID Connect with ASP.NET Core in a docker container environment, you may encounter issues with the public authority URL being not accessible inside of the container, causing the setup to fail.

    In my test case, it's a Sitecore 10.1 XP environment with Sitecore Identity. Internal Identity URL is http://id but the external Identity URL is https://identity.clientname.com.

    This is only one of the possible solutions.

    In summary, this solution is to hijack when requests are made in the application related to OpenID connect, and replace external authority URLs with internal ones.

    I am not covering the overall setup of OpenID connect in ASP.NET Core, there are enough articles about it on the internet already.

    Create a custom HttpClientHandler like below:

    using System;
    using System.Net.Http;
    using System.Threading;
    using System.Threading.Tasks;

    namespace Custom
    {
        /// <summary>
        /// Handles all the backend channel communications to Identity and replace any public identity url with internal url
        /// </summary>
        public class CustomHttpMessageHandler : HttpClientHandler
        {
            private string _authority { get; set; }
            private string _internalAuthority { get; set; }

            public CustomHttpMessageHandler(string authority, string internalAuthority)
            {
                _authority = authority;
                _internalAuthority = internalAuthority;
            }

            protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
            {
                request.RequestUri = new Uri(request.RequestUri.OriginalString.Replace(_authority, _internalAuthority));
                return base.SendAsync(request, cancellationToken);
            }
        }
    }

    In the startup.cs, replace the default BackchannelHttpHandler with the custom one:

    ...
    public void ConfigureServices(IServiceCollection services)
            {
                var internalAuthority = "http://id";
                var authority = "https://identity.clientname.com";

                ...
                services
                ...
                .AddOpenIdConnect(cfg =>
                {
                    cfg.Authority = authority;
                    ...
                    cfg.RequireHttpsMetadata = false;
                    cfg.BackchannelHttpHandler = new CustomHttpMessageHandler(authority, internalAuthority);
                    ...
                   
                    cfg.Events.OnRedirectToIdentityProvider = async context =>
                    {
                        if (context.ProtocolMessage.RedirectUri.StartsWith("http://"))
                        {
                            context.ProtocolMessage.RedirectUri = context.ProtocolMessage.RedirectUri.Replace("http://", "https://");
                        }
                        await Task.FromResult(0);
                    };
                    ...
                }
              ...
              }
    ...

    Replace the URLs, ideally, get them from configurations. Note that the handling of http vs https since internal is normally http and external is https.

    After the setup, all the requests inside of docker related to OpenID connect will use the internal URL and browser URLs will be proper public https versions.



  • Switching to FastMail after 15 years of Gmail

    If you are not in the mood for a story, feel free to jump to the FastMail review section down below.

    Since I had my first Gmail account in 2006, over the years I have registered many more. Plus other Outlook and Office365 emails. I always wanted a centralised place online to access all the accounts but I never found a solution that I liked.

    Recently a Telegram bot became popular among online Chinese communities, it can query all the leaked personal data based on emails, website usernames, and mobile numbers etc. I have always known that things are being leaked online via various hacks but I never thought it is this serious.

    It had everything! My passwords, security questions, emails, mobile numbers, national identity number (of China) and rough locations I have surfed the internet over the years when I was in China.

    The conclusion I came to after that: to guarantee personal data security in this era
    • For every service and website, different emails and passwords shall be used.
    • Security questions should also be dynamically generated passwords that are stored in a password manager.

    I have been using LastPass for a long time so the password part is solved already. When it comes to email, not so much.

    I have about 15 emails addresses currently with Gmail and Outlook/Office365. One of the Gmail accounts is still the free legacy GSuite plan and is bind to a custom domain of mine. The problem with that domain is that it is a .US domain that doesn't support privacy protection and has to expose my personal contact information in WHOIS databases.

    Therefore the plan was to
    • Get a new, not-too-long, cheap domain that supports WHOIS guard
    • Switch to a PAID email service for the custom domains and manage all email accounts in one single place. Then gradually change all emails to the custom ones.

    FastMail seems to be a good choice. After trialling it for less than a day, I select the Standard plan and started migrating all my email accounts and setup.

    Here are my takes:

    Migration


    • Emails from 15 Gmail and Outlook/Office365 accounts were imported. Can not import iCloud emails, only contacts and calendars. The migration processes were smooth.


    Using Other Emails in One Place


    • IMAP and SMTP can be set up automatically when importing email accounts.
    • IMAP default poll interval is 1 hour when the web interface is not open. It's a bit long. So I changed all email accounts to forwarding, this allows me to set up rules to mark them as read as well.
      • IMAP settings can be disabled without deleting them.
      • Note that if an email is marked as spam in the original email account, it won't be forwarded. Since normally spam are deleted in 30 days, there is a slight chance of losing important emails. Very slight...
      • Since SMTP was set up during import, you still can send emails with those accounts.


    FastMail Features


    Speed
    • FastMail is not fast... It is not slow, but the web interface is sometimes stuck. iOS app seems to be an advanced web app so sometimes it loads for a while. That being said, it's not an issue. It's fast enough, just not lightning fast.

    Custom Domains
    • I set up 2 custom domains. One of them has DNS servers set to FastMail's so the setup was really easy. And since they manage the DNS, it makes setup easier for other features too like sub-domains and websites etc...

    Websites
    • This was a surprise to me since I didn't know they had this feature. Basically allows you to host static websites under your custom domains or your FastMail accounts. When DNS is managed by them as mentioned above, no CNAME setup is required at all. With just a few clicks, the website is up.
    • Not much use for me at this point, but I am sure this would come in handy one day.

    Labels v.s. Folders
    • You can choose to use Labels or Folders. I chose Labels since I like to mark emails with multiple labels and labels can be nested too so still support a tree structure.

    Storage
    • I used about 6GB for all emails. The Standard plan comes with 30GB, which should be enough for me for the next few decades...

    Email Clients
    • The setup on iPhone was just a scan of a QR code! It sets up email, calendar, contacts, notes and reminders altogether. So I can replace all the Google services on the phone which is great. However I couldn't find reminders from iOS on the web interface, so I disabled the feature on iPhone.
    • Haven't tested other email clients yet but I guess they should all work fine?

    Labelling wildcard emails use To address
    • One of the main benefits of having a custom domain email for me is to be able to use any email address with that domain and all the emails go into one single mailbox. It is very handy so I can use different email addresses for different services/websites without any pre-setup required.

      It does create a challenge to identify which email is for which address when looking at the mailbox. So in Google, I built App Scripts to automatically label incoming emails with the email address from To address.

      FastMail doesn't support this out of the box. However, they do support the Sieve code and it seems to be powerful enough to do the things I wanted. The problem is that I have never heard of it before and am still figuring out how to write the script. I am also trying to get help from their Support for this. Anyway, this wouldn't be a big issue if it can't be done automatically. I can always manually set up the rules afterwards.

    Updates: Fastmail support actually helped me with the Sieve code that's required and it does exactly what I wanted!

    if header :matches "To" "*@domain.tld"
    {
    set "foldername" "${1}";
    fileinto :copy :create "INBOX.${foldername}";
    stop;
    }​

    Search
    • Couldn't figure out a way to search for emails without any labels assigned yet...  Use "in:Archive" in search would give all emails with any labels!
    • There was one time in iOS, old emails that are irrelevant show up in front in search results. Couldn't replicate on the web or the 2nd day in iOS. Could be a bug...

    Support
    • Got a reply overnight, although my question was misunderstood, which could be my bad... Anyway, the response time is good.
      • Updates: they actually helped me with the Sieve code that implements the custom feature I wanted, very nice and helpful!


    Overall, I am happy with what I have so far. The Standard plan costs about 76 AUD a year which is very inexpensive. I didn't mention anything about security and privacy because, to be honest, there is no way for me to verify,  just have to take their words for it.

    The only thing left for me to do now is to change all emails in different services and websites. I think this is going to take a while, 10 years maybe...

    Updates: been using it for almost two months now, still happy.

  • Sitecore SXA Sites Generate URLs with Incorrect Hostnames

    Came across a weird issue with a client's Sitecore instance recently where on CD instances, internal link URLs generated in Navigation or redirects are absolute URLs including hostnames but they are CM hostname instead of CDs'.

    The Sitecore version is 10.1. SXA and Commerce Storefront. On Docker and AKS.

    After lots of digging and debugging Sitecore DLLs within docker containers, found the issue and another finding.

    1. CD generates URLs with CM hostname is because SXA site settings don't have a language set


    Debugging through the code of LinkManager.GetItemUrl, it reaches Sitecore.Links.UrlBuilders.Helpers.OptionsDecoratedSiteResolver.ResolveSite at some point like below:

    // Sitecore.Links.UrlBuilders.Helpers.OptionsDecoratedSiteResolver 
    using Sitecore.Data.Items; 
    using Sitecore.Diagnostics; 
    using Sitecore.Web; 
    /// <inheritdoc /> 
    /// <summary> 
    /// Resolves the site that best matches the given item. 
    /// </summary> 
    /// <param name="item">Requested item that we need to resolve matching site.</param> 
    /// <returns></returns> 
    public SiteInfo ResolveSite(Item item) 
    Assert.ArgumentNotNull(item, "item"); 
    SiteInfo defaultSiteInfo = GetDefaultSiteInfo(); 
    if (SkipResolving(item)) 
    return defaultSiteInfo; 
    return _realSiteResolver.ResolveSite(item) ?? defaultSiteInfo; 
    }

    if SkipResolving is true, it takes site context from the default which is correct.

    However, it is false in this case and it goes to SiteResolver to resolve the site via item only. So there is a chance that the item is resolved to the wrong site since both CM and CD are pointing to the same Sitecore item path and who gets resolved is all dependent on the order of the sites.

    But why SkipResolving is false?

    // Sitecore.Links.UrlBuilders.Helpers.OptionsDecoratedSiteResolver 
    using Sitecore.Data.Items; 
    private bool SkipResolving(Item item) 
    if (!NoNeedToResolveSite(item) && !OptionsSiteIsDifferentThanContextSite()) 
    return ItemMatchesCurrentSite(item); 
    return true; 
    }
    .....
    protected virtual bool ItemMatchesCurrentSite(Item item)
    {
    if (!Settings.Rendering.SiteResolvingMatchCurrentSite)
    {
    return false;
    }
    if (PathMatchesContextSite(item))
    {
    return LanguageMatchesContextSite(item);
    }
    return false;
    }
    .....
    private bool LanguageMatchesContextSite(Item item)
    {
    string name = item.Language.Name;
    string language = ContextSite.Language;
    if (Settings.Rendering.SiteResolvingMatchCurrentLanguage)
    {
    return name.Equals(language, StringComparison.InvariantCultureIgnoreCase);
    }
    return true;
    }

    Out of all the code here, LanguageMatchesContextSite is false because of ContextSite.Language is empty but item.Language.Name is "en".

    It's all because "Language" field of site settings item(such as "/sitecore/content/tenant/Sites/siteA/Settings/Site Grouping/siteA-CM") is blank... After setting this field to "en", the issue is no longer presented.

    1. Must set "TargetHostname" if "Hostname" contains pipes or wildcards in SXA site settings


    This one was not causing a direct issue however could potentially be one. The GetTargetHostName method on SiteInfo is like below:

    // Sitecore.Web.SiteInfo
    /// <summary>
    /// Gets the name of the target host.
    /// </summary>
    internal virtual string GetTargetHostName()
    {
    if (!string.IsNullOrEmpty(TargetHostName))
    {
    return TargetHostName;
    }
    if (HostName.IndexOfAny(new char[2]
    {
    '*',
    '|'
    }) < 0)
    {
    return HostName;
    }
    return string.Empty;
    }

    It's easy to see that if Hostname contains any wildcards or pipes and TargetHostName is blank, it would just return an empty string. Which would definitely cause issues somewhere on the site.
  • Hands on with Sitecore Experience Edge for Content Hub

    One of the exciting news in the Sitecore world recently was the release of Sitecore Experience Edge for Content Hub (Experience Edge for XM is coming later this year, so will focus on Content Hub for now). If you haven't heard the news yet, I strongly recommend watching this Youtube video to see it in real action. And here is the official Experience Edge™ for Content Hub documentation for a more thorough understanding.

    To get a hands-on demo experience with Sitecore Experience Edge for Content Hub, please talk to your Sitecore contacts for accessing the Content Hub Sandbox portal. The information below also applies to the Production setup, just ignore the parts related to sandboxes.

    1. You should receive the instructions and accesses to create a sandbox Content Hub environment, just ensure the following when creating the sandbox
      1. Select version 4.0.0
      2. Select "Content Publishing" license
    2. When CH sandbox is up and running, head to Settings->PublishingSettings and ensure "Publishing enabled" is ticked and Save.
    3. Ensure that there are Content Collections and Content that are in the Final state (This is for viewing them in Delivery API)
    4. Ensure that the Content Collections and Content have the green tick cloud icon that says "Published to delivery platform"
    5. You can generate API tokens from Settings->API Keys or from the Content Collections
    6. Delivery API endpoint for demo containers is https://edge-beta.sitecorecloud.io/api/graphql/v1 
    7. Preview API endpoint for demo container is https://[[Your demo instance hostname]]/api/graphql/preview/v1

    Note: For Production, the Delivery API endpoint is https://edge.sitecorecloud.io/api/graphql/v1

    You can use Postman to test Delivery and Preview APIs too. Below is an export of a Postman collection:

    {
    "info": {
    "_postman_id": "66f4925b-b6f0-4ed1-bea3-bdb09ba57045",
    "name": "Experience Edge For CH",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
    },
    "item": [
    {
    "name": "Preview API",
    "request": {
    "method": "POST",
    "header": [
    {
    "key": "X-GQL-Token",
    "value": "",
    "type": "text"
    }
    ],
    "body": {
    "mode": "graphql",
    "graphql": {
    "query": "{\r\n  allM_ContentCollection {\r\n    results {\r\n      contentCollectionName\r\n      id\r\n      publishStatus\r\n      contentCollectionToContent{\r\n        results{\r\n          content_Name\r\n          content_PublicationDate\r\n          publishStatus\r\n        }\r\n      }\r\n    }\r\n  }\r\n}",
    "variables": ""
    },
    "options": {
    "raw": {
    "language": "json"
    }
    }
    },
    "url": {
    "raw": "https:///api/graphql/preview/v1",
    "protocol": "https",
    "host": [
    ""
    ],
    "path": [
    "api",
    "graphql",
    "preview",
    "v1"
    ]
    }
    },
    "response": []
    },
    {
    "name": "Delivery API - Beta",
    "request": {
    "method": "POST",
    "header": [
    {
    "key": "X-GQL-Token",
    "value": "",
    "type": "text"
    }
    ],
    "body": {
    "mode": "graphql",
    "graphql": {
    "query": "{\r\n  allM_ContentCollection {\r\n    results {\r\n      contentCollectionName\r\n      id\r\n      publishStatus\r\n      contentCollectionToContent{\r\n        results{\r\n          content_Name\r\n          content_PublicationDate\r\n          publishStatus\r\n        }\r\n      }\r\n    }\r\n  }\r\n}",
    "variables": ""
    },
    "options": {
    "raw": {
    "language": "json"
    }
    }
    },
    "url": {
    "raw": "https://edge-beta.sitecorecloud.io/api/graphql/v1",
    "protocol": "https",
    "host": [
    "edge-beta",
    "sitecorecloud",
    "io"
    ],
    "path": [
    "api",
    "graphql",
    "v1"
    ]
    }
    },
    "response": []
    }
    ]
    }
     
    3 variables are:
    1. hostname
    2. PreviewToken
    3. DeliveryToken

    Have fun!
  • 2021

    The past year has been a weird year. It went by without much me feeling the time passing. Things happened around the world, but something also happened in our personal life.

    2020 was not too bad to be honest. We are positive people and of course 2021 will be even better.
  • Sitecore Application Insights Setup Checklist in Azure App Services

  • Sitecore Installation Framework and Sitecore Versions Compatibility Table

  • Synonyms Support with Azure Search in Sitecore SXA Search Component

  • Sitecore Website Federated Authentication with Azure AD B2C with OpenID Connect

  • Evernote, Jekyll Static Site, Azure Storage, Azure CDN, Cloudflare and Azure DevOps

  • Cannot disable QoS in ASUS Wireless Router Web GUI

  • Azure DevOps - Cloud Warmup for Sitecore

  • Sitecore Patch Finder

  • Sitecore Helix Documentation version 2 in ePub format

  • Use one license.xml for all local Sitecore instances with SymbolicLink

  • Where is Site Order stored in Sitecore SXA Site Manager Powershell tool

  • Model Rendering Variant doesnt work with custom Sitecore SXA rendering

  • Common Causes and Troubleshooting Sitecore App Restarts on Azure Web App Services

  • Sitecore 9.1 Initial Release with Identify Server User Boost Feature Patch

  • Sitecore Solr Linq Error RANGE_GOOP while using String CompareTo

  • Missing sxacontent_txm in Sitecore SXA Solr Index with Commerce Installed

  • Generating static 500 error html pages using SXA in Sitecore

  • Whitelist Hosted Agent IP address in AzureDevOps pipeline (For Sitecore Unicorn Sync)

  • Setup Postman with Local Sitecore xConnect services

  • Evil Inline Aspx Pages for Sitecore Development

  • Fast Query return null in Sitecore instances

  • Sitecore Campaign Creator API 500 error caused by Sitecore Instance Manager

  • Delivery instance doesn't clear cache with Sitecore Publishing Service

  • Use the same session database for both Priavte and Shared Session States in Sitecore

  • How to handle different Sitecore Module instllation scenarios with WebDeploy approach

  • All about Sitecore.Cloud.Integration.Bootload

  • Blogging from Evernote

  • TTS with Google Home on Home Assistant stopped working

  • Deploy Sitecore Azure AppService Packages on Local IIS

  • Google Home working with Yeelight with Xiaomi Smart Wireless Switch

  • Thoughts on Sitecore Experience Accelerator

  • Sitecore Consulting in the most down-to-earth way

  • Issues while setting up Continuous Integration and Delivery with Visual Studio Team Services for Xamarin.iOS Apps

  • Warning in Sitecore Media Library Folder Grid View

  • Sitecore Content/MVT Testing doesn't update in Delivery Instances

  • HockeyApp, Azure Mobile Engagement and Visual Studio Application Insights

  • 1 MB file upload size limit for WFFM on CD instance

  • Sitecore web.config include patching sub-folder orders

  • Sitecore Shell Content Testing Web API returns 404/403 error while creating page tests/content tests

  • Hello From The Engine

  • A New Back Engine for Blog

  • AnyoneHue? - Monitor multiple people with WIFI for Philips Hue controlling

  • Converting Chinese to Pinyin/Quanpin in Xamarin IOS development

  • Get Sitecore Item SiteInfo

  • Root Australia Vodafone Samsung Galaxy Note 4(N910G) Android Lollipop 5.0.1

  • Input Method On Galaxy Note 4 Keep Reverting Back To Default

  • Onda v820w flash os - Booting to droidboot.img failed

  • Amazon Unlimited Cloud, Windows Home Server, Netflix and VPN

  • Sitecore session expires(timeout) quickly(1 min) when not logged in

  • Browser requests page twice

  • From mobile

  • Focusing on content

  • Section references in Sitecore config files with ref attribute

  • Powerline Ethernet Adapters

  • TDS project build fail in Teamcity with VS 2012 runner

  • Sonos with Airsonos on Windows and Mac

  • Google Tag Manager causing placeholder issues in IE 8

  • Must have softwares after reinstalling windows

  • Multi-Site solution deployments in Azure

  • Schedule tasks not running on CDS in Sitecore

  • IncludeTemplates and ExcludeTemplates warning for custom index of Sitecore

  • Sitecore Redirect Manager

  • Sitecore Indexes media url with /sitecore/shell

  • IIS Http error 500.19 with web.config

subscribe via RSS