c# - Why do I get SecurityTokenSignatureKeyNotFoundException? -
when try pass jwt (issued azure mobile services) http header/authorization/bearer token:
header: { "alg": "hs256", "typ": "jwt", "kid": "0" } claims: { "ver": 2, "aud": "facebook", "iss": "urn:microsoft:windows-azure:zumo", "urn:microsoft:credentials": "pyk8b5...", "exp": 1436730730, "uid": "facebook:10000xxxxxxxxxx" }
into asp.net web api configured:
const string issuer = "urn:microsoft:windows-azure:zumo"; byte[] mobileservicessecret = textencodings.base64url.decode(configurationmanager.appsettings["as:secretkey"]); app.usejwtbearerauthentication( new jwtbearerauthenticationoptions { authenticationmode = authenticationmode.active, allowedaudiences = new[] { "facebook" }, issuersecuritytokenproviders = new iissuersecuritytokenprovider[] { new symmetrickeyissuersecuritytokenprovider(issuer, mobileservicessecret) } });
i get:
a first chance exception of type 'system.identitymodel.tokens.securitytokensignaturekeynotfoundexception' occurred in system.identitymodel.tokens.jwt.dll
i suspect because presence of "kid" property?
edit: using https://github.com/magenic/jwtvalidator/tree/master/jwtvalidator/jwtvalidator , it's possible validate jwt, it's nothing wrong it. want use owin/katana.
google suggests following - calling tokeninfo endpoint
rather writing own code perform these verification steps, recommend using google api client library platform, or calling our tokeninfo validation endpoint.
to validate id token using tokeninfo endpoint, make https post or request endpoint, , pass id token in id_token parameter. example, validate token "xyz123", make following request:
customjwthandler.cs
using system; using system.collections.generic; using system.identitymodel.tokens; using system.linq; using system.net.http; using system.web; using system.web.configuration; using newtonsoft.json; using system.net; using system.threading.tasks; using system.threading; using services.models; using system.security.claims; namespace services { /// <summary> /// implementation of google jwt verification /// demonstrates: /// - jwt validation /// </summary> /// @author kunal.bajpai@gmail.com (kunal bajpai) public class customjwthandler : delegatinghandler { private const string url_google_token_info = "https://www.googleapis.com/oauth2/v3/tokeninfo"; /// <summary> /// /// </summary> /// <param name="request"></param> /// <param name="cancellationtoken"></param> /// <returns></returns> protected override task<httpresponsemessage> sendasync(httprequestmessage request, cancellationtoken cancellationtoken) { httpstatuscode statuscode; string token; var authheader = request.headers.authorization; if (authheader == null) { // missing authorization header return base.sendasync(request, cancellationtoken); } if (!tryretrievetoken(request, out token)) { return task<httpresponsemessage>.factory.startnew(() => new httpresponsemessage(httpstatuscode.unauthorized)); } try { validatetoken(token); return base.sendasync(request, cancellationtoken); } catch (securitytokeninvalidaudienceexception) { statuscode = httpstatuscode.unauthorized; } catch (securitytokenvalidationexception) { statuscode = httpstatuscode.unauthorized; } catch (exception) { statuscode = httpstatuscode.internalservererror; } return task<httpresponsemessage>.factory.startnew(() => new httpresponsemessage(statuscode)); } /// <summary> /// validates jwt token /// </summary> /// <param name="jwttoken"></param> private void validatetoken(string jwttoken) { try { using (webclient wc = new webclient()) { tokeninfo tokeninfo = jsonconvert.deserializeobject<tokeninfo>(wc.downloadstring(url_google_token_info + "?id_token=" + jwttoken)); claimsprincipal claimsprincipal = new claimsprincipal(new claimsidentity(extractclaims(tokeninfo), tokeninfo.issuer)); thread.currentprincipal = claimsprincipal; httpcontext.current.user = claimsprincipal; } } catch (webexception e) { httpstatuscode statuscode = ((httpwebresponse)e.response).statuscode; if (statuscode == httpstatuscode.badrequest) { throw new securitytokenvalidationexception(); } else { throw new exception(); } } } /// <summary> /// tries retrieve token /// </summary> /// <param name="request"></param> /// <param name="token"></param> /// <returns></returns> private static bool tryretrievetoken(httprequestmessage request, out string token) { token = null; ienumerable<string> authorizationheaders; if (!request.headers.trygetvalues("authorization", out authorizationheaders) || authorizationheaders.count() > 1) { return false; } var bearertoken = authorizationheaders.elementat(0); token = bearertoken.startswith("bearer ") ? bearertoken.substring(7) : bearertoken; return true; } private list<claim> extractclaims(tokeninfo tokeninfo) { list<claim> claims = new list<claim> { new claim(claimtypes.name, tokeninfo.name), new claim(claimtypes.email, tokeninfo.email), new claim(claimtypes.givenname, tokeninfo.givenname), new claim(claimtypes.surname, tokeninfo.familyname), new claim(applicationuser.claim_type_locale, tokeninfo.locale), new claim(claimtypes.nameidentifier, tokeninfo.providerkey, claimvaluetypes.string, tokeninfo.issuer), new claim(applicationuser.claim_type_email_confirmed, tokeninfo.isemailverifed.tostring(), claimvaluetypes.boolean) }; return claims; } } }
tokeninfo.cs
using microsoft.aspnet.identity.entityframework; using newtonsoft.json; namespace services.models { public class tokeninfo { [jsonproperty("iss")] public string issuer { get; set; } [jsonproperty("aud")] public string audienceclientid { get; set; } [jsonproperty("sub")] public string providerkey { get; set; } [jsonproperty("email_verified")] public bool isemailverifed { get; set; } [jsonproperty("azp")] public string androidclientid { get; set; } [jsonproperty("email")] public string email { get; set; } [jsonproperty("iat")] public long issuedat { get; set; } [jsonproperty("exp")] public long expiresat { get; set; } [jsonproperty("name")] public string name { get; set; } [jsonproperty("picture")] public string picture { get; set; } [jsonproperty("given_name")] public string givenname { get; set; } [jsonproperty("family_name")] public string familyname { get; set; } [jsonproperty("locale")] public string locale { get; set; } [jsonproperty("alg")] public string algorithm { get; set; } [jsonproperty("kid")] public string kid { get; set; } public override bool equals(object obj) { if (obj.gettype() != typeof(applicationuser)) { return false; } applicationuser user = (applicationuser)obj; bool haslogin = false; foreach (identityuserlogin login in user.logins) { if (login.providerkey == providerkey) { haslogin = true; break; } } if (!haslogin) { return false; } if (user.firstname != givenname) { return false; } if (user.lastname != familyname) { return false; } if (user.locale != locale) { return false; } return base.equals(obj); } } }
webapiconfig.cs
using system; using system.collections.generic; using system.linq; using system.net.http; using system.web.http; using microsoft.owin.security.oauth; using newtonsoft.json.serialization; namespace services { public static class webapiconfig { public static void register(httpconfiguration config) { // web api configuration , services // configure web api use bearer token authentication. config.suppressdefaulthostauthentication(); config.filters.add(new hostauthenticationfilter(oauthdefaults.authenticationtype)); // web api routes config.maphttpattributeroutes(); config.routes.maphttproute( name: "defaultapi", routetemplate: "api/{controller}/{id}", defaults: new { id = routeparameter.optional } ); config.messagehandlers.add(new customjwthandler()); } } }
Comments
Post a Comment