• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ibm-cloud-security / appid-clientsdk-js / 8018892406

23 Feb 2024 12:04PM UTC coverage: 93.066%. Remained the same
8018892406

push

github

web-flow
Merge pull request #99 from ibm-cloud-security/abod-akhras-patch-2

Update dry-publish.js to use Node v20

78 of 94 branches covered (82.98%)

Branch coverage included in aggregate %.

177 of 180 relevant lines covered (98.33%)

4.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.54
/src/utils.js
1
const jsrsasign = require('jsrsasign');
1✔
2
const AppIDError = require('./errors/AppIDError');
1✔
3
const OAuthError = require('./errors/OAuthError');
1✔
4
const RequestHandler = require('./RequestHandler');
1✔
5
const TokenValidator = require('./TokenValidator');
1✔
6
const constants = require('./constants');
1✔
7

8
class Utils {
9
        constructor(
10
                {
×
11
                        requestHandler = new RequestHandler(),
×
12
                        tokenValidator = new TokenValidator(),
×
13
                        url = URL,
×
14
                        openIdConfigResource,
15
                        popup,
16
                        jsrsasign = jsrsasign
×
17
                } = {}) {
18
                this.URL = url;
1✔
19
                this.request = requestHandler.request;
1✔
20
                this.tokenValidator = tokenValidator;
1✔
21
                this.openIdConfigResource = openIdConfigResource;
1✔
22
                this.popup = popup;
1✔
23
                this.rs = jsrsasign;
1✔
24
        };
25

26
        buildParams(params) {
27
                return Object.keys(params).map(function (key) {
7✔
28
                        return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]);
50✔
29
                }).join('&');
30
        };
31

32
        getRandomString(length) {
33
                return this.rs.KJUR.crypto.Util.getRandomHexOfNbytes(length / 2);
15✔
34
        };
35

36
        sha256(message) {
37
                return this.rs.KJUR.crypto.Util.sha256(message);
5✔
38
        }
39

40
        getPKCEFields() {
41
                const codeVerifier = this.getRandomString(constants.CODE_VERIFIER_LENGTH);
5✔
42
                const codeChallenge = this.sha256(codeVerifier);
5✔
43
                const state = this.getRandomString(constants.STATE_LENGTH);
5✔
44
                const nonce = this.getRandomString(constants.NONCE_LENGTH);
5✔
45
                return {codeVerifier, codeChallenge, state, nonce};
5✔
46
        }
47

48
        getAuthParamsAndUrl({clientId, origin, prompt, endpoint, userId, changeDetailsCode}) {
49
                const {codeVerifier, codeChallenge, state, nonce} = this.getPKCEFields();
4✔
50
                let authParams = {
4✔
51
                        client_id: clientId,
52
                        response_type: constants.RESPONSE_TYPE,
53
                        state: this.rs.stob64(state),
54
                        code_challenge: this.rs.stob64(codeChallenge),
55
                        code_challenge_method: constants.CHALLENGE_METHOD,
56
                        redirect_uri: origin,
57
                        response_mode: constants.RESPONSE_MODE,
58
                        nonce,
59
                        scope: constants.SCOPE
60
                };
61

62
                if (prompt) {
4✔
63
                        authParams.prompt = prompt;
1✔
64
                }
65

66
                if (userId) {
4✔
67
                        authParams.user_id = userId;
2✔
68
                }
69

70
                if (changeDetailsCode) {
4!
71
                        authParams.code = changeDetailsCode;
×
72
                }
73

74
                const url = endpoint + '?' + this.buildParams(authParams);
4✔
75
                return {
4✔
76
                        codeVerifier,
77
                        nonce,
78
                        state,
79
                        url
80
                };
81
        }
82

83
        async performOAuthFlowAndGetTokens({userId, origin, clientId, endpoint, changeDetailsCode}) {
84
                const {codeVerifier, state, nonce, url} = this.getAuthParamsAndUrl({userId, origin, clientId, endpoint, changeDetailsCode});
1✔
85

86
                this.popup.open();
1✔
87
                this.popup.navigate(url);
1✔
88
                const message = await this.popup.waitForMessage({messageType: 'authorization_response'});
1✔
89
                this.popup.close();
1✔
90
                this.verifyMessage({message, state});
1✔
91
                let authCode = message.data.code;
1✔
92

93
                return await this.retrieveTokens({
1✔
94
                        clientId,
95
                        authCode,
96
                        codeVerifier,
97
                        nonce,
98
                        windowOrigin: origin
99
                });
100
        }
101

102
        verifyMessage({message, state}) {
103
                if (message.data.error || message.data.error_description) {
5✔
104
                        throw new OAuthError({description: message.data.error_description, error: message.data.error});
1✔
105
                }
106

107
                if (this.rs.b64utos(message.data.state) !== state) {
4✔
108
                        throw new AppIDError(constants.INVALID_STATE);
1✔
109
                }
110

111
                if (message.origin !== new this.URL(this.openIdConfigResource.getAuthorizationEndpoint()).origin) {
3✔
112
                        throw new AppIDError(constants.INVALID_ORIGIN);
1✔
113
                }
114
        }
115

116
        async retrieveTokens({clientId, authCode, nonce, codeVerifier, windowOrigin}) {
117
                let issuer = this.openIdConfigResource.getIssuer();
2✔
118
                let params = {
2✔
119
                        grant_type: 'authorization_code',
120
                        redirect_uri: windowOrigin,
121
                        code: authCode,
122
                        code_verifier: codeVerifier
123
                };
124

125
                const requestParams = this.buildParams(params);
2✔
126
                const tokenEndpoint = this.openIdConfigResource.getTokenEndpoint();
2✔
127

128
                const tokens = await this.request(tokenEndpoint, {
2✔
129
                        method: 'POST',
130
                        headers: {
131
                                'Authorization': 'Basic ' + this.rs.stob64(`${clientId}:${codeVerifier}`),
132
                                'Content-Type': 'application/x-www-form-urlencoded'
133
                        },
134
                        body: requestParams
135
                });
136
                const publicKeys = await this.openIdConfigResource.getPublicKeys();
2✔
137

138
                const accessTokenPayload = this.tokenValidator.decodeAndValidate({
2✔
139
                        token: tokens.access_token,
140
                        publicKeys,
141
                        issuer,
142
                        clientId
143
                });
144

145
                const idTokenPayload = this.tokenValidator.decodeAndValidate({
2✔
146
                        token: tokens.id_token,
147
                        publicKeys,
148
                        issuer,
149
                        clientId,
150
                        nonce
151
                });
152

153
                return {accessToken: tokens.access_token, accessTokenPayload, idToken: tokens.id_token, idTokenPayload};
2✔
154
        }
155
}
156

157
module.exports = Utils;
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc