Search code examples
c#model-view-controllersingle-sign-onmicrosoft-teams

Custom Teams Person Tab's SSO no longer working


We have a solution which will be posted below!

Our custom Teams tab application uses SSO for logging in. This wasn't working properly in Teams Classic for a client. We then discovered that didn't work in the new Teams Public Preview (2023).

The Application has a Welcome page where the _LoginPartial then determines if you are in the Teams Desktop application, Teams web or a browser. If it's in Teams Desktop, it then attempts to use SSO get the access token. After which, it then runs a block of code that contains the following:

if (accessToken != null)
{
    DelegateAuthenticationProvider providerX = new DelegateAuthenticationProvider(
        async (requestMessage) =>
        {
            requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken.ToString());
        });
    var retrievedToken = new JwtSecurityToken(jwtEncodedString: accessToken);                                
    string retrievedTenantId = retrievedToken.Claims.First(c => c.Type == "tid").Value;
                                    
    GraphServiceClient graphClient = new GraphServiceClient(providerX);
    User me = await graphClient.Me.Request().GetAsync();
    var org = await graphClient.Organization.Request().GetAsync();
                
    //* Configure claims to mimic the log in from normal the desktop login.
    //* *** This should probably be in a separate function ***
    var claims = new List<Claim>();
    claims.Add(new Claim("http://schemas.microsoft.com/identity/claims/tenantid", org.First().Id));
    claims.Add(new Claim(ClaimTypes.GivenName, me.GivenName));
    claims.Add(new Claim(ClaimTypes.Surname, me.Surname));
    claims.Add(new Claim("http://schemas.microsoft.com/identity/claims/objectidentifier", me.Id));
    claims.Add(new Claim(ClaimTypes.Upn, me.UserPrincipalName));
    claims.Add(new Claim(ClaimTypes.Name, me.GivenName + " " + me.Surname));
    claims.Add(new Claim(ClaimTypes.Email, me.UserPrincipalName));
    claims.Add(new Claim(ClaimTypes.Role, "IsTeamsApp"));
    claims.Add(new Claim("accessToken", accessToken));
    
    var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
    var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);

    var authenticationProperties = new AuthenticationProperties();
    var authToken = new AuthenticationToken();
    authToken.Name = "bearer";
    authToken.Value = accessToken ;
    var tokens = new List<AuthenticationToken>() { authToken };

    authenticationProperties.StoreTokens(tokens); 
     
    //* This was added during troubleshooting to help determine 
    //* if authentication was done within the Teams desktop app
    authenticationProperties.Items.Add(".AuthScheme", "msTeamsApp");

    await HttpContext.SignInAsync("Cookies", claimsPrincipal, authenticationProperties);
    
    Response.StatusCode = (int)HttpStatusCode.OK;
    _logger.LogTrace("Exiting Method");
    return Json(new { accessToken = accessToken });
}
else
{
    _logger.LogTrace("Exiting Method: Error");
    throw new Exception("accessToken could not be found.");
}

We then spent about 3 weeks trying to figure out why the application wouldn't work for some clients, but our logging wasn't helping (it was broken and we had to fix it). So, we started exploring all of the materials that Microsoft as updated to no avail. We even took a look at the Teams Toolkit, but that was using Razor pages for a Single-Page Application (SPA) and we had an C# MVC, Multiple Page Application solution, so it wasn't caching the Access token the way we needed. We also noticed that the cache wasn't always updating.


Solution

  • Another developer checked our code that we developed to figure out SSO. It functioned properly so he did a comparison of the code and found that the issue didn't occur when we excluded authenticationProperties from HttpContext.SignInAsync.

    After we whittled this down, we found that removing this line of code fixes the problem:

    //authenticationProperties.StoreTokens(tokens); 
    

    We surmise that the cause of this problem was that the new Teams Public Preview (2023) and the updated Teams Classic was changed from a Chromium backend to Edge. We believe that Edge is enforcing the max cookie length (4096 bytes) whereas Chromium might not have been. The token we were storing was 2222 characters.

    Furthermore, we do not know what consequence may result from not using StoreTokens. However, our issue seems to be fixed.