0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-02-24 22:05:56 -05:00

chore(connector-wechat*): fix wechat connectors error handler, comments and UTs (#1020)

* fix(connector-wechat*): remove unnecessary comments

* fix(connector-wechat*): fix wechat connectors error handler, comments and UTs
This commit is contained in:
Darcy Ye 2022-06-07 12:06:37 +08:00 committed by GitHub
parent 458602fd64
commit 5a5083e863
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 55 additions and 62 deletions

View file

@ -57,15 +57,25 @@ describe('getAccessToken', () => {
expect(openid).toEqual('openid'); expect(openid).toEqual('openid');
}); });
it('throws SocialAuthCodeInvalid error if accessToken not found in response', async () => { it('throws SocialAuthCodeInvalid error if errcode is 40029', async () => {
nock(accessTokenEndpointUrl.origin) nock(accessTokenEndpointUrl.origin)
.get(accessTokenEndpointUrl.pathname) .get(accessTokenEndpointUrl.pathname)
.query(parameters) .query(parameters)
.reply(200, {}); .reply(200, { errcode: 40_029, errmsg: 'invalid code' });
await expect(weChatNativeMethods.getAccessToken('code')).rejects.toMatchError( await expect(weChatNativeMethods.getAccessToken('code')).rejects.toMatchError(
new ConnectorError(ConnectorErrorCodes.SocialAuthCodeInvalid) new ConnectorError(ConnectorErrorCodes.SocialAuthCodeInvalid)
); );
}); });
it('throws error if errcode is neither 40029 nor undefined', async () => {
nock(accessTokenEndpointUrl.origin)
.get(accessTokenEndpointUrl.pathname)
.query(true)
.reply(200, { errcode: 40_163, errmsg: 'invalid code' });
await expect(weChatNativeMethods.getAccessToken('wrong_code')).rejects.toMatchError(
new Error('invalid code')
);
});
}); });
describe('validateConfig', () => { describe('validateConfig', () => {
@ -140,15 +150,16 @@ describe('getUserInfo', () => {
}); });
it('throws error if `openid` is missing', async () => { it('throws error if `openid` is missing', async () => {
nock.cleanAll();
nock(userInfoEndpointUrl.origin).get(userInfoEndpointUrl.pathname).query(parameters).reply(0, {
unionid: 'this_is_an_arbitrary_wechat_union_id',
headimgurl: 'https://github.com/images/error/octocat_happy.gif',
nickname: 'wechat bot',
});
nockNoOpenIdAccessTokenResponse(); nockNoOpenIdAccessTokenResponse();
nock(userInfoEndpointUrl.origin)
.get(userInfoEndpointUrl.pathname)
.query(parameters)
.reply(200, {
errcode: 41_009,
errmsg: 'missing openid',
});
await expect(weChatNativeMethods.getUserInfo({ code: 'code' })).rejects.toMatchError( await expect(weChatNativeMethods.getUserInfo({ code: 'code' })).rejects.toMatchError(
new ConnectorError(ConnectorErrorCodes.SocialAuthCodeInvalid) new Error('missing openid')
); );
}); });
@ -156,7 +167,7 @@ describe('getUserInfo', () => {
nock(userInfoEndpointUrl.origin) nock(userInfoEndpointUrl.origin)
.get(userInfoEndpointUrl.pathname) .get(userInfoEndpointUrl.pathname)
.query(parameters) .query(parameters)
.reply(200, { errcode: 40_001 }); .reply(200, { errcode: 40_001, errmsg: 'invalid credential' });
await expect(weChatNativeMethods.getUserInfo({ code: 'code' })).rejects.toMatchError( await expect(weChatNativeMethods.getUserInfo({ code: 'code' })).rejects.toMatchError(
new ConnectorError(ConnectorErrorCodes.SocialAccessTokenInvalid) new ConnectorError(ConnectorErrorCodes.SocialAccessTokenInvalid)
); );

View file

@ -31,9 +31,6 @@ import {
WeChatNativeConfig, WeChatNativeConfig,
} from './types'; } from './types';
// As creating a WeChat Web/Mobile application needs a real App or Website record, the real test is temporarily not finished.
// TODO: test with our own wechat mobile/web application (LOG-1910), already tested with other verified wechat web application
export default class WeChatNativeConnector implements SocialConnector { export default class WeChatNativeConnector implements SocialConnector {
public metadata: ConnectorMetadata = defaultMetadata; public metadata: ConnectorMetadata = defaultMetadata;
@ -68,6 +65,7 @@ export default class WeChatNativeConnector implements SocialConnector {
access_token: accessToken, access_token: accessToken,
openid, openid,
errcode, errcode,
errmsg,
} = await got } = await got
.get(accessTokenEndpoint, { .get(accessTokenEndpoint, {
searchParams: { appid, secret, code, grant_type: 'authorization_code' }, searchParams: { appid, secret, code, grant_type: 'authorization_code' },
@ -75,10 +73,8 @@ export default class WeChatNativeConnector implements SocialConnector {
}) })
.json<AccessTokenResponse>(); .json<AccessTokenResponse>();
assert( assert(errcode !== 40_029, new ConnectorError(ConnectorErrorCodes.SocialAuthCodeInvalid));
errcode !== 40_029 && accessToken && openid, assert(!errcode && accessToken && openid, new Error(errmsg ?? ''));
new ConnectorError(ConnectorErrorCodes.SocialAuthCodeInvalid)
);
return { accessToken, openid }; return { accessToken, openid };
}; };
@ -87,15 +83,6 @@ export default class WeChatNativeConnector implements SocialConnector {
const { code } = codeDataGuard.parse(data); const { code } = codeDataGuard.parse(data);
const { accessToken, openid } = await this.getAccessToken(code); const { accessToken, openid } = await this.getAccessToken(code);
// TO-DO: @Darcy refactor this
// 'openid' is defined as a required input argument in WeChat API doc, but it does not necessarily have to
// be the return value from getAccessToken per testing.
// In other words, 'openid' is required but the response of getUserInfo is consistent as long as
// access_token is valid.
// We are expecting to get 41009 'missing openid' response according to the developers doc, but the
// fact is that we still got 40001 'invalid credentials' response.
assert(openid, new Error('`openid` is required by WeChat API.'));
try { try {
const { unionid, headimgurl, nickname, errcode, errmsg } = await got const { unionid, headimgurl, nickname, errcode, errmsg } = await got
.get(userInfoEndpoint, { .get(userInfoEndpoint, {

View file

@ -15,6 +15,7 @@ export type AccessTokenResponse = {
refresh_token?: string; refresh_token?: string;
scope?: string; scope?: string;
errcode?: number; errcode?: number;
errmsg?: string;
}; };
export type UserInfoResponse = { export type UserInfoResponse = {

View file

@ -57,15 +57,25 @@ describe('getAccessToken', () => {
expect(openid).toEqual('openid'); expect(openid).toEqual('openid');
}); });
it('throws SocialAuthCodeInvalid error if accessToken not found in response', async () => { it('throws SocialAuthCodeInvalid error if errcode is 40029', async () => {
nock(accessTokenEndpointUrl.origin) nock(accessTokenEndpointUrl.origin)
.get(accessTokenEndpointUrl.pathname) .get(accessTokenEndpointUrl.pathname)
.query(parameters) .query(parameters)
.reply(200, {}); .reply(200, { errcode: 40_029, errmsg: 'invalid code' });
await expect(weChatMethods.getAccessToken('code')).rejects.toMatchError( await expect(weChatMethods.getAccessToken('code')).rejects.toMatchError(
new ConnectorError(ConnectorErrorCodes.SocialAuthCodeInvalid) new ConnectorError(ConnectorErrorCodes.SocialAuthCodeInvalid)
); );
}); });
it('throws error if errcode is neither 40029 nor undefined', async () => {
nock(accessTokenEndpointUrl.origin)
.get(accessTokenEndpointUrl.pathname)
.query(true)
.reply(200, { errcode: 40_163, errmsg: 'invalid code' });
await expect(weChatMethods.getAccessToken('wrong_code')).rejects.toMatchError(
new Error('invalid code')
);
});
}); });
describe('validateConfig', () => { describe('validateConfig', () => {
@ -84,18 +94,9 @@ describe('validateConfig', () => {
const nockNoOpenIdAccessTokenResponse = () => { const nockNoOpenIdAccessTokenResponse = () => {
const accessTokenEndpointUrl = new URL(accessTokenEndpoint); const accessTokenEndpointUrl = new URL(accessTokenEndpoint);
const parameters = new URLSearchParams({ nock(accessTokenEndpointUrl.origin).get(accessTokenEndpointUrl.pathname).query(true).reply(200, {
appid: '<app-id>', access_token: 'access_token',
secret: '<app-secret>',
code: 'code',
grant_type: 'authorization_code',
}); });
nock(accessTokenEndpointUrl.origin)
.get(accessTokenEndpointUrl.pathname)
.query(parameters)
.reply(200, {
access_token: 'access_token',
});
}; };
describe('getUserInfo', () => { describe('getUserInfo', () => {
@ -107,6 +108,7 @@ describe('getUserInfo', () => {
code: 'code', code: 'code',
grant_type: 'authorization_code', grant_type: 'authorization_code',
}); });
nock(accessTokenEndpointUrl.origin) nock(accessTokenEndpointUrl.origin)
.get(accessTokenEndpointUrl.pathname) .get(accessTokenEndpointUrl.pathname)
.query(parameters) .query(parameters)
@ -141,15 +143,16 @@ describe('getUserInfo', () => {
}); });
it('throws error if `openid` is missing', async () => { it('throws error if `openid` is missing', async () => {
nock.cleanAll();
nock(userInfoEndpointUrl.origin).get(userInfoEndpointUrl.pathname).query(parameters).reply(0, {
unionid: 'this_is_an_arbitrary_wechat_union_id',
headimgurl: 'https://github.com/images/error/octocat_happy.gif',
nickname: 'wechat bot',
});
nockNoOpenIdAccessTokenResponse(); nockNoOpenIdAccessTokenResponse();
nock(userInfoEndpointUrl.origin)
.get(userInfoEndpointUrl.pathname)
.query(parameters)
.reply(200, {
errcode: 41_009,
errmsg: 'missing openid',
});
await expect(weChatMethods.getUserInfo({ code: 'code' })).rejects.toMatchError( await expect(weChatMethods.getUserInfo({ code: 'code' })).rejects.toMatchError(
new ConnectorError(ConnectorErrorCodes.SocialAuthCodeInvalid) new Error('missing openid')
); );
}); });
@ -157,7 +160,7 @@ describe('getUserInfo', () => {
nock(userInfoEndpointUrl.origin) nock(userInfoEndpointUrl.origin)
.get(userInfoEndpointUrl.pathname) .get(userInfoEndpointUrl.pathname)
.query(parameters) .query(parameters)
.reply(200, { errcode: 40_001 }); .reply(200, { errcode: 40_001, errmsg: 'invalid credential' });
await expect(weChatMethods.getUserInfo({ code: 'code' })).rejects.toMatchError( await expect(weChatMethods.getUserInfo({ code: 'code' })).rejects.toMatchError(
new ConnectorError(ConnectorErrorCodes.SocialAccessTokenInvalid) new ConnectorError(ConnectorErrorCodes.SocialAccessTokenInvalid)
); );

View file

@ -28,7 +28,7 @@ import {
import { weChatConfigGuard, AccessTokenResponse, UserInfoResponse, WeChatConfig } from './types'; import { weChatConfigGuard, AccessTokenResponse, UserInfoResponse, WeChatConfig } from './types';
// As creating a WeChat Web/Mobile application needs a real App or Website record, the real test is temporarily not finished. // As creating a WeChat Web/Mobile application needs a real App or Website record, the real test is temporarily not finished.
// TODO: test with our own wechat mobile/web application (LOG-1910), already tested with other verified wechat web application // TODO: test with our own WeChat web application (LOG-2719), already tested with other verified WeChat web application
export default class WeChatConnector implements SocialConnector { export default class WeChatConnector implements SocialConnector {
public metadata: ConnectorMetadata = defaultMetadata; public metadata: ConnectorMetadata = defaultMetadata;
@ -64,6 +64,7 @@ export default class WeChatConnector implements SocialConnector {
access_token: accessToken, access_token: accessToken,
openid, openid,
errcode, errcode,
errmsg,
} = await got } = await got
.get(accessTokenEndpoint, { .get(accessTokenEndpoint, {
searchParams: { appid, secret, code, grant_type: 'authorization_code' }, searchParams: { appid, secret, code, grant_type: 'authorization_code' },
@ -71,10 +72,8 @@ export default class WeChatConnector implements SocialConnector {
}) })
.json<AccessTokenResponse>(); .json<AccessTokenResponse>();
assert( assert(errcode !== 40_029, new ConnectorError(ConnectorErrorCodes.SocialAuthCodeInvalid));
errcode !== 40_029 && accessToken && openid, assert(!errcode && accessToken && openid, new Error(errmsg ?? ''));
new ConnectorError(ConnectorErrorCodes.SocialAuthCodeInvalid)
);
return { accessToken, openid }; return { accessToken, openid };
}; };
@ -83,15 +82,6 @@ export default class WeChatConnector implements SocialConnector {
const { code } = codeDataGuard.parse(data); const { code } = codeDataGuard.parse(data);
const { accessToken, openid } = await this.getAccessToken(code); const { accessToken, openid } = await this.getAccessToken(code);
// TO-DO: @Darcy refactor this
// 'openid' is defined as a required input argument in WeChat API doc, but it does not necessarily have to
// be the return value from getAccessToken per testing.
// In other words, 'openid' is required but the response of getUserInfo is consistent as long as
// access_token is valid.
// We are expecting to get 41009 'missing openid' response according to the developers doc, but the
// fact is that we still got 40001 'invalid credentials' response.
assert(openid, new Error('`openid` is required by WeChat API.'));
try { try {
const { unionid, headimgurl, nickname, errcode, errmsg } = await got const { unionid, headimgurl, nickname, errcode, errmsg } = await got
.get(userInfoEndpoint, { .get(userInfoEndpoint, {
@ -105,7 +95,7 @@ export default class WeChatConnector implements SocialConnector {
// 'errmsg' and 'errcode' turn to non-empty values or empty values at the same time. Hence, if 'errmsg' is non-empty then 'errcode' should be non-empty. // 'errmsg' and 'errcode' turn to non-empty values or empty values at the same time. Hence, if 'errmsg' is non-empty then 'errcode' should be non-empty.
assert(errcode !== 40_001, new ConnectorError(ConnectorErrorCodes.SocialAccessTokenInvalid)); assert(errcode !== 40_001, new ConnectorError(ConnectorErrorCodes.SocialAccessTokenInvalid));
assert(!errcode, new Error(errmsg)); assert(!errcode, new Error(errmsg ?? ''));
return { id: unionid ?? openid, avatar: headimgurl, name: nickname }; return { id: unionid ?? openid, avatar: headimgurl, name: nickname };
} catch (error: unknown) { } catch (error: unknown) {

View file

@ -11,6 +11,7 @@ export type AccessTokenResponse = {
refresh_token?: string; refresh_token?: string;
scope?: string; scope?: string;
errcode?: number; errcode?: number;
errmsg?: string;
}; };
export type UserInfoResponse = { export type UserInfoResponse = {