This is the application with which we are testing silent renew: https://zamboni-app.azurewebsites.net.
Login works as expected and results in two localStorage values:
Key: oidc.097c6062ed5547928aea963cc456607f
Value: {"id":"097c6062ed5547928aea963cc456607f","created":1479635448,"nonce":"9c51be022d7d4171a69e553ea3ad64e7","authority":"https://zamboni-auth.azurewebsites.net","client_id":"Aurelia.OpenIdConnect"}
Key: oidc.user:https://zamboni-auth.azurewebsites.net:Aurelia.OpenIdConnect
Value: {"id_token":"eyJhbGciOiJSUzI1NiIo...Gs5JtV5C_aBbA","access_token":"CfDJ8C4hTU...pMgdinWZcQYc","token_type":"Bearer","scope":"openid email profile roles","profile":{"unique_name":"[email protected]","AspNet.Identity.SecurityStamp":"2228d4ae-5865-4c22-80b8-8afcd140d0a3","sub":"b2618ce0-d2c1-49a9-90ac-a57191c18f9a","jti":"0b4f532d-19aa-4f3b-b46c-704833c470e2","usage":"id_token","azp":"Aurelia.OpenIdConnect"},"expires_at":1479635449}
When we do a silent login, the following request occurs, and it does not include the id_token_hint.
https://zamboni-auth.azurewebsites.net/connect/authorize
?client_id=Aurelia.OpenIdConnect
&redirect_uri=https%3A%2F%2Fzamboni-app.azurewebsites.net%2Fsignin-oidc
&response_type=id_token%20token
&scope=openid%20email%20roles%20profile
&state=6c48e3c0b728414f9d1adf2f5bc99a01
&nonce=8b576d7184f04f27bab43c5ea510cdf5
&prompt=none
What does getUser() return for id_token?
This is what getUser() returns.
{
"id_token":"eyJhbGciOiJSUzI...Us9Q5YID0X06RjTdo7noKqHluiyvW1joAPsQKRQ",
"access_token":"CfDJ8C4hTU...XDnlU",
"token_type":"Bearer",
"scope":"openid email profile roles",
"profile":{
"unique_name":"[email protected]",
"AspNet.Identity.SecurityStamp":"2228d4ae-5865-4c22-80b8-8afcd140d0a3",
"sub":"b2618ce0-d2c1-49a9-90ac-a57191c18f9a",
"jti":"bc05202c-2443-4fca-a45f-32e13a639a3a",
"usage":"id_token",
"azp":"Aurelia.OpenIdConnect"
},
"expires_at":1479751017
}
This is the full id_token:
eyJhbGciOiJSUzI1NiIsImtpZCI6IllaTElZQ1ZVVFBITlRIWVU0RUwwVEQ5WkNJUUJLODM4TUNKLTBSTVQiLCJ0eXAiOiJKV1QifQ.eyJ1bmlxdWVfbmFtZSI6InNoYXVuQGJpZ2ZvbnQuY2EiLCJBc3BOZXQuSWRlbnRpdHkuU2VjdXJpdHlTdGFtcCI6IjIyMjhkNGFlLTU4NjUtNGMyMi04MGI4LThhZmNkMTQwZDBhMyIsInN1YiI6ImIyNjE4Y2UwLWQyYzEtNDlhOS05MGFjLWE1NzE5MWMxOGY5YSIsImp0aSI6ImJjMDUyMDJjLTI0NDMtNGZjYS1hNDVmLTMyZTEzYTYzOWEzYSIsInVzYWdlIjoiaWRfdG9rZW4iLCJhdWQiOiJBdXJlbGlhLk9wZW5JZENvbm5lY3QiLCJub25jZSI6IjQwMWRiMmUzNjhhYzQ2ZTZhMjg5MDI0NmZkMjJmNGM4IiwiYXRfaGFzaCI6InBzOGFGYThVRWJNenl4YmRfOTBSV1EiLCJhenAiOiJBdXJlbGlhLk9wZW5JZENvbm5lY3QiLCJuYmYiOjE0Nzk3NTEwMDUsImV4cCI6MTQ3OTc1MTAzNSwiaWF0IjoxNDc5NzUxMDA1LCJpc3MiOiJodHRwczovL3phbWJvbmktYXV0aC5henVyZXdlYnNpdGVzLm5ldC8ifQ.LVLAAYFadwSp4nvij6IoKt8uJTwY2t6SnfU9xui2aMKfTbacDwI58xMOsplE8HvqOdMM5k6A_ucCmJ2DdrxfQkWSved_VZeF6Hwd07dMNU5f5uGsTXvAxWe67gWPp1eB5evSdtMTROgDBySluMxIUc9KAv_UW92GGZoRz5w26sR-EqQHtMyLDosKXPeA0kGMzT4ZwWerqPJnYVvzPFwMkEyLQiIGfLyH24TL0Wqj5rTBgXdebnWzvpM9162thGH9b1Haqluk6aLxOcieW6BeYGypjF3PZ1dN7AbwD7U1ZNrYsXiUs9Q5YID0X06RjTdo7noKqHluiyvW1joAPsQKRQ
This is the decoded id_token:
{
alg: "RS256",
kid: "YZLIYCVUTPHNTHYU4EL0TD9ZCIQBK838MCJ-0RMT",
typ: "JWT"
}.
{
unique_name: "[email protected]",
AspNet.Identity.SecurityStamp: "2228d4ae-5865-4c22-80b8-8afcd140d0a3",
sub: "b2618ce0-d2c1-49a9-90ac-a57191c18f9a",
jti: "bc05202c-2443-4fca-a45f-32e13a639a3a",
usage: "id_token",
aud: "Aurelia.OpenIdConnect",
nonce: "401db2e368ac46e6a2890246fd22f4c8",
at_hash: "ps8aFa8UEbMzyxbd_90RWQ",
azp: "Aurelia.OpenIdConnect",
nbf: 1479751005,
exp: 1479751035,
iat: 1479751005,
iss: "https://zamboni-auth.azurewebsites.net/"
}.
[signature]
At the time of attempting silent login, the current time is 1479751778, which means that the id_token has expired. I need to read the OpenID Connect specification to learn whether we can use an expired id_token as an id_token_hint.
This is the code that is calling getUser() to acquire the above data.
this.openIdConnect.userManager.getUser().then(function(t) {
"undefined" != typeof t && null !== t && (t.expired ? e.loginSilent() : e.inMemoryUser = t)
})
I need to read the OpenID Connect specification to learn whether we can use an expired id_token as an id_token_hint.
You can.
As for expiration logic in the oidc-client library, we don't check expiration of the id_token when determining to send it or not. The expiration flags on the user have to do with the access token (not the id token).
I looked thru the code, and if you can see an id_token on the getUser(), then that should be passed when doing silent renews. You will need to debug further on your side.
I'll be adding some data points and [ hypotheses ] as they arise.
dev build does not have the error, while the prod build does have it. Thru debugging steps the issue has resolved and we are no longer able to reproduce it. It might have been a caching issue that kept delivering an older, broken release. Thank you for answering our questions along the way, especially that we can use an expired id_token as an id_token_hint.
@brockallen You wrote:
As for expiration logic in the oidc-client library, we don't check expiration of the id_token when determining to send it or not. The expiration flags on the user have to do with the access token (not the id token).
Thank you for clarifying that, and I have a follow up question to check my understanding. oidc-client-js validates the id_token once, and that is before adding it to web storage. After that, oidc-client-js does not need to re-validate the id_token. Now, if my-app (which is using oidc-client-js) decides to send the id_token to another component of my-app (e.g. a server-side component), then that component must validate the id_token before using the information in it. Is that right?
From the spec:
If any of the validation procedures defined in this document fail, any operations requiring the information that failed to correctly validate MUST be aborted and the information that failed to validate MUST NOT be used.
http://openid.net/specs/openid-connect-implicit-1_0.html#IDToken
From Google
One thing that makes ID tokens useful is that fact that you can pass them around different components of your app. These components can use an ID token as a lightweight authentication mechanism authenticating the app and the user. But before you can use the information in the ID token or rely on it as an assertion that the user has authenticated, you must validate it.
https://developers.google.com/identity/protocols/OpenIDConnect#validatinganidtoken
Thank you for clarifying that, and I have a follow up question to check my understanding. oidc-client-js validates the id_token once, and that is before adding it to web storage. After that, oidc-client-js does not need to re-validate the id_token.
Correct.
Now, if my-app (which is using oidc-client-js) decides to send the id_token to another component of my-app (e.g. a server-side component), then that component must validate the id_token before using the information in it. Is that right?
Yes, but this is not the normal use case for an id_token so I worry that this approach might get overused/abused. But how your server-side code does its validation is up to its threat model.
Thank you. We will be circumspect in our passing around of the id_token.