1+ import Log from './Log' ;
2+ import MetadataService from './MetadataService' ;
3+ import UserInfoService from './UserInfoService' ;
4+
5+ const ProtocolClaims = [ "nonce" , "at_hash" , "iat" , "nbf" , "exp" , "aud" , "iss" , "c_hash" ] ;
6+
7+ export default class ResponseValidator {
8+
9+ constructor ( settings , MetadataServiceCtor = MetadataService , UserInfoServiceCtor = UserInfoService ) {
10+ if ( ! settings ) {
11+ Log . error ( "No settings passed to ResponseValidator" ) ;
12+ throw new Error ( "settings" ) ;
13+ }
14+
15+ this . _settings = settings ;
16+ this . _metadataService = new MetadataServiceCtor ( this . _settings ) ;
17+ this . _userInfoService = new UserInfoServiceCtor ( this . _settings ) ;
18+ }
19+
20+ validateSigninResponse ( state , response ) {
21+ Log . info ( "ResponseValidator.validateSigninResponse" ) ;
22+
23+ if ( state . id !== response . state ) {
24+ Log . error ( "State does not match" ) ;
25+ return Promise . reject ( new Error ( "State does not match" ) ) ;
26+ }
27+
28+ // now that we know the state matches, take the stored data
29+ // and set it into the response so callers can get their state
30+ // this is important for both success & error outcomes
31+ response . state = state . data ;
32+
33+ if ( response . error ) {
34+ Log . warn ( "Response was error" , response . error ) ;
35+ return Promise . resolve ( response ) ;
36+ }
37+
38+ if ( state . nonce && ! response . id_token ) {
39+ Log . error ( "Expecting id_token in response" ) ;
40+ return Promise . reject ( new Error ( "No id_token in response" ) ) ;
41+ }
42+
43+ let p = null ;
44+
45+ if ( response . id_token ) {
46+ if ( response . access_token ) {
47+ Log . info ( "Validating id_token and access_token" ) ;
48+ p = validateIdTokenAndAccessToken ( state , response ) ;
49+ }
50+ else {
51+ Log . info ( "Validating id_token" ) ;
52+ p = validateIdToken ( state , response ) ;
53+ }
54+ }
55+ else {
56+ Log . info ( "No id_token to validate" ) ;
57+ p = Promise . resolve ( response ) ;
58+ }
59+
60+ return p . then ( response => {
61+ response . profile = this . filterProtocolClaims ( response . profile ) ;
62+
63+ // if (this._settings.loadUserInfo) {
64+
65+ // Log.info("loading user info");
66+ // return this._userInfoService.getClaims(response.access_token).then(claims => {
67+
68+ // Log.info("user info claims received");
69+ // response.profile = this.mergeClaims(response.profile, claims);
70+
71+ // return response;
72+ // });
73+ // }
74+
75+ return response ;
76+ } ) ;
77+ }
78+
79+ // mergeClaims(claims1, claims2){
80+ // return claims1;
81+ // }
82+
83+ filterProtocolClaims ( claims ) {
84+ if ( claims && this . _settings . filterProtocolClaims ) {
85+ ProtocolClaims . forEach ( type => {
86+ delete claims [ type ] ;
87+ } ) ;
88+
89+ Log . info ( "protocol claims filtered" , claims ) ;
90+ }
91+ return claims ;
92+ }
93+
94+ validateIdTokenAndAccessToken ( state , response ) {
95+ return this . validateIdToken ( state , response ) . then ( response => {
96+ return this . validateAccessToken ( response ) ;
97+ } ) ;
98+ }
99+
100+ validateIdToken ( state , response ) {
101+ return Promise . resolve ( response ) ;
102+
103+ // log("OidcClient.validateIdTokenAsync");
104+
105+ // var client = this;
106+ // var settings = client._settings;
107+
108+ // return client.loadX509SigningKeyAsync().then(function (cert) {
109+
110+ // var jws = new KJUR.jws.JWS();
111+ // if (jws.verifyJWSByPemX509Cert(id_token, cert)) {
112+ // var id_token_contents = JSON.parse(jws.parsedJWS.payloadS);
113+
114+ // if (nonce !== id_token_contents.nonce) {
115+ // return error("Invalid nonce");
116+ // }
117+
118+ // return client.loadMetadataAsync().then(function (metadata) {
119+
120+ // if (id_token_contents.iss !== metadata.issuer) {
121+ // return error("Invalid issuer");
122+ // }
123+
124+ // if (id_token_contents.aud !== settings.client_id) {
125+ // return error("Invalid audience");
126+ // }
127+
128+ // var now = parseInt(Date.now() / 1000);
129+
130+ // // accept tokens issues up to 5 mins ago
131+ // var diff = now - id_token_contents.iat;
132+ // if (diff > (5 * 60)) {
133+ // return error("Token issued too long ago");
134+ // }
135+
136+ // if (id_token_contents.exp < now) {
137+ // return error("Token expired");
138+ // }
139+
140+ // if (access_token && settings.load_user_profile) {
141+ // // if we have an access token, then call user info endpoint
142+ // return client.loadUserProfile(access_token, id_token_contents).then(function (profile) {
143+ // return copy(profile, id_token_contents);
144+ // });
145+ // }
146+ // else {
147+ // // no access token, so we have all our claims
148+ // return id_token_contents;
149+ // }
150+
151+ // });
152+ // }
153+ // else {
154+ // return error("JWT failed to validate");
155+ // }
156+
157+ // });
158+ }
159+
160+ validateAccessToken ( response ) {
161+ return Promise . resolve ( response ) ;
162+
163+ // log("OidcClient.validateAccessTokenAsync");
164+
165+ // if (!id_token_contents.at_hash) {
166+ // return error("No at_hash in id_token");
167+ // }
168+
169+ // var hash = KJUR.crypto.Util.sha256(access_token);
170+ // var left = hash.substr(0, hash.length / 2);
171+ // var left_b64u = hextob64u(left);
172+
173+ // if (left_b64u !== id_token_contents.at_hash) {
174+ // return error("at_hash failed to validate");
175+ // }
176+
177+ // return resolve();
178+ }
179+ }
0 commit comments