How to get the id_token in blazor web assembly

北城以北 提交于 2020-08-10 19:54:51

问题


I have got a Blazor WebAssembly (latest 3.2.0) app with oidc Authentication. The asp.net authentication provides a way to get the accessToken but can't see any means to access the id_token (jwt) which is required for my scenario. I can see the id_token in the local storage of the browser. What would be best way to access it?

Thanks


回答1:


You can read it from the session storage using JSInterop, it is stored at key oidc.user:{app baseUri}:{app client id} :

@inject IJSRuntime JSRuntime
@inject NavigationManager NavigationManager
...
@code {
     private async Task<string> ReadIdToken()
     {
          const string clientId = "your oidc client id";
          var userDataKey = $"oidc.user:{NavigationManager.BaseUri}:{clientId}";
          var userData = await JSRuntime.InvokeAsync<UserData>("sessionStorage.getItem", userDataKey);
          return userData.id_token;          
     }

     class UserData
     {
         public string id_token { get; set; }
         public int expires_at { get; set; }
     }
}



回答2:


Here's a working code sample that allows you to get the id_token in raw format as well as a list of claims parsed from it.

Note: You should authenticate before you can see the results...

@page "/"

@inject IJSRuntime JSRuntime
@inject NavigationManager NavigationManager

@using System.Security.Claims
@using System.Text.Json

<p>@JwtToken</p>

@foreach (var claim in claims)
{
    <p>@claim</p>
}


@code {
      List<Claim> claims = new List<Claim>();
      string JwtToken;


    protected override async Task OnInitializedAsync()
    {
        await GetJwtToken();

    }
    private async Task GetJwtToken()
    {
        var baseUri = NavigationManager.BaseUri.Substring(0, 
                              NavigationManager.BaseUri.Length - 1);
        // client id example: RoleBasedApiAuthorization.Client 
        const string clientID = "<Place here your client id>";
        var key = $"oidc.user:{baseUri}:{clientID}";
        JwtToken = await JSRuntime.InvokeAsync<string> 
                                    ("sessionStorage.getItem", key);

        if (JwtToken != null)
        {
            claims = ParseClaimsFromJwt(JwtToken).ToList();
        }

    }


    public IEnumerable<Claim> ParseClaimsFromJwt(string jwt)
    {
        var payload = jwt.Split('.')[1];
        var jsonBytes = ParseBase64WithoutPadding(payload);
        var keyValuePairs = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonBytes);
        return keyValuePairs.Select(kvp => new Claim(kvp.Key, kvp.Value.ToString()));
    }

    private byte[] ParseBase64WithoutPadding(string base64)
    {
        switch (base64.Length % 4)
        {
            case 2: base64 += "=="; break;
            case 3: base64 += "="; break;
        }
        return Convert.FromBase64String(base64);
    }
}



回答3:


Thank you guys SO much for this - I've been banging my head against this for a week (doh - forgot to look at the browser session data in Chrome to think about using JRRuntime...).

I'm not sure if this is Cognito-specific, but the key for me is not using the NavigationManager BaseUri, but the OIDC Authority.

@page "/"
@using System.Text.Json
@inject IJSRuntime JSRuntime

<AuthorizeView>
    <Authorized>
        <div>
            <b>CachedAuthSettings</b>
            <pre>
                @JsonSerializer.Serialize(authSettings, indented);
            </pre>
            <br/>
            <b>CognitoUser</b><br/>
            <pre>
                @JsonSerializer.Serialize(user, indented);
            </pre>
        </div>
    </Authorized>
    <NotAuthorized>
        <div class="alert alert-warning" role="alert">
            Everything requires you to <a href="/authentication/login">Log In</a> first.
        </div>
    </NotAuthorized>
</AuthorizeView>

@code {

    JsonSerializerOptions indented = new JsonSerializerOptions() { WriteIndented = true };
    CachedAuthSettings authSettings;
    CognitoUser user;

    protected override async Task OnInitializedAsync()
    {
        string key = "Microsoft.AspNetCore.Components.WebAssembly.Authentication.CachedAuthSettings";
        string authSettingsRAW = await JSRuntime.InvokeAsync<string>("sessionStorage.getItem", key);
        authSettings = JsonSerializer.Deserialize<CachedAuthSettings>(authSettingsRAW);
        string userRAW = await JSRuntime.InvokeAsync<string>("sessionStorage.getItem", authSettings?.OIDCUserKey);
        user = JsonSerializer.Deserialize<CognitoUser>(userRAW);
    }

    public class CachedAuthSettings
    {
        public string authority { get; set; }
        public string metadataUrl { get; set; }
        public string client_id { get; set; }
        public string[] defaultScopes { get; set; }
        public string redirect_uri { get; set; }
        public string post_logout_redirect_uri { get; set; }
        public string response_type { get; set; }
        public string response_mode { get; set; }
        public string scope { get; set; }

        public string OIDCUserKey => $"oidc.user:{authority}:{client_id}";
    }

    public class CognitoUser
    {
        public string id_token { get; set; }
        public string access_token { get; set; }
        public string refresh_token { get; set; }
        public string token_type { get; set; }
        public string scope { get; set; }
        public int expires_at { get; set; }
    }
}

I get serialization errors if I directly try and convert the string to classes using JSRuntme.InvokeAsync but it works fine with the JsonSerializer, that's why you see that seemingly extra step there.



来源:https://stackoverflow.com/questions/63105618/how-to-get-the-id-token-in-blazor-web-assembly

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!