Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(auth): add a default deviceName when remembering device #13022

Merged
merged 6 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,21 @@ describe('fetchDevices', () => {
const dateEpoch = 1.696296885807e9;
const date = new Date(dateEpoch * 1000);
const clientResponseDevice = {
DeviceAttributes: [{ Name: 'attributeName', Value: 'attributeValue' }],
DeviceAttributes: [
{ Name: 'attributeName', Value: 'attributeValue' },
{ Name: 'device_name', Value: 'deviceNameValue' },
],
DeviceCreateDate: dateEpoch,
DeviceKey: 'DeviceKey',
DeviceLastAuthenticatedDate: dateEpoch,
DeviceLastModifiedDate: dateEpoch,
};
const apiOutputDevice = {
id: 'DeviceKey',
name: undefined,
name: 'deviceNameValue',
attributes: {
attributeName: 'attributeValue',
device_name: 'deviceNameValue',
},
createDate: date,
lastModifiedDate: date,
Expand Down
3 changes: 3 additions & 0 deletions packages/auth/src/providers/cognito/apis/fetchDevices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,11 @@ const parseDevicesResponse = async (
DeviceLastModifiedDate,
DeviceLastAuthenticatedDate,
}) => {
let deviceName: string | undefined;
const attributes = DeviceAttributes.reduce(
(attrs: any, { Name, Value }) => {
if (Name && Value) {
if (Name === 'device_name') deviceName = Value;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it needed to assert for this specific device name ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The deviceName is returned by the service call in DeviceAttributes on the device_name attribute. We were doing the same in V5 as well

attrs[Name] = Value;
}

Expand All @@ -72,6 +74,7 @@ const parseDevicesResponse = async (

return {
id,
name: deviceName,
attributes,
createDate: DeviceCreateDate
? new Date(DeviceCreateDate * 1000)
Expand Down
2 changes: 2 additions & 0 deletions packages/auth/src/providers/cognito/utils/signInHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
AuthAction,
assertTokenProviderConfig,
base64Encoder,
getDeviceName,
} from '@aws-amplify/core/internals/utils';

import { ClientMetadata, ConfirmSignInOptions } from '../types';
Expand Down Expand Up @@ -1072,6 +1073,7 @@ export async function getNewDeviceMetatada(
{ region: getRegion(userPoolId) },
{
AccessToken: accessToken,
DeviceName: await getDeviceName(),
DeviceKey: newDeviceMetadata?.DeviceKey,
DeviceSecretVerifierConfig: deviceSecretVerifierConfig,
},
Expand Down
4 changes: 2 additions & 2 deletions packages/aws-amplify/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@
"name": "[Auth] confirmSignIn (Cognito)",
"path": "./dist/esm/auth/index.mjs",
"import": "{ confirmSignIn }",
"limit": "28.10 kB"
"limit": "28.26 kB"
},
{
"name": "[Auth] updateMFAPreference (Cognito)",
Expand Down Expand Up @@ -449,7 +449,7 @@
"name": "[Auth] Basic Auth Flow (Cognito)",
"path": "./dist/esm/auth/index.mjs",
"import": "{ signIn, signOut, fetchAuthSession, confirmSignIn }",
"limit": "29.90 kB"
"limit": "30.06 kB"
},
{
"name": "[Auth] OAuth Auth Flow (Cognito)",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/libraryUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export { amplifyUuid } from './utils/amplifyUuid';
export { AmplifyUrl, AmplifyUrlSearchParams } from './utils/amplifyUrl';
export { parseAmplifyConfig } from './utils/parseAmplifyConfig';
export { getClientInfo } from './utils';
export { getDeviceName } from './utils/deviceName';

// Auth utilities
export {
Expand Down
15 changes: 15 additions & 0 deletions packages/core/src/utils/deviceName/getDeviceName.native.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { getDeviceName as getDeviceNameNative } from '@aws-amplify/react-native';

/**
* Retrieves the device name using name in ios and model in android,
*
* @returns {Promise<string>} A promise that resolves with a string representing the device name.
*
* Example Output:
* ios: 'iPhone' / 'user's iPhone'
* android: 'sdk_gphone64_arm64'
*/
export const getDeviceName = async (): Promise<string> => getDeviceNameNative();
52 changes: 52 additions & 0 deletions packages/core/src/utils/deviceName/getDeviceName.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { NavigatorUA } from './types';
/**
* Retrieves the device name using the User-Agent Client Hints API if available,
* falling back to the traditional userAgent string if not.
*
* @returns {Promise<string>} A promise that resolves with a string representing the device name.
*
* Example Output:
* navigator.userAgentData:
* 'macOS 14.2.1 arm macOS Not A(Brand/99.0.0.0;Google Chrome/121.0.6167.160;Chromium/121.0.6167.160'
* navigator.userAgent:
* 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/115.0'
*/
export const getDeviceName = async (): Promise<string> => {
const { userAgentData } = navigator as NavigatorUA;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

userAgentData is experimental and hence isn't typed but is officially recommended by google.

The support for userAgentData is still limited and so we have a fall back to option to use userAgent

Since userAgentData is experimental can we just use userAgent across all browsers ?

No, using only userAgent would result in chrome triggering Audit usage of navigator.userAgent, navigator.appVersion, and navigator.platform and reopening an old issue.


if (!userAgentData) return navigator.userAgent;

const {
platform = '',
platformVersion = '',
model = '',
architecture = '',
fullVersionList = [],
} = await userAgentData.getHighEntropyValues([
'platform',
'platformVersion',
'architecture',
'model',
'fullVersionList',
]);

const versionList = fullVersionList
.map((v: { brand: string; version: string }) => `${v.brand}/${v.version}`)
.join(';');

const deviceName = [
platform,
platformVersion,
architecture,
model,
platform,
versionList,
]
.filter(value => value)
.join(' ');

return deviceName;
};
4 changes: 4 additions & 0 deletions packages/core/src/utils/deviceName/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

export { getDeviceName } from './getDeviceName';
44 changes: 44 additions & 0 deletions packages/core/src/utils/deviceName/types.ts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not opposed to creating our own types, but I wonder if there's a pre-built @types package we could use if it's well-maintained?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Took a reference of the types from this package user-agent-data-types but since it was less widely used, added the types directly

Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

// WICG Spec: https://wicg.github.io/ua-client-hints

// https://wicg.github.io/ua-client-hints/#navigatorua
export interface NavigatorUA {
readonly userAgentData?: NavigatorUAData;
}

// https://wicg.github.io/ua-client-hints/#dictdef-navigatoruabrandversion
interface NavigatorUABrandVersion {
readonly brand: string;
readonly version: string;
}

// https://wicg.github.io/ua-client-hints/#dictdef-uadatavalues
interface UADataValues {
readonly brands?: NavigatorUABrandVersion[];
readonly mobile?: boolean;
readonly platform?: string;
readonly architecture?: string;
readonly bitness?: string;
readonly formFactor?: string[];
readonly model?: string;
readonly platformVersion?: string;
/** @deprecated in favour of fullVersionList */
readonly uaFullVersion?: string;
readonly fullVersionList?: NavigatorUABrandVersion[];
readonly wow64?: boolean;
}

// https://wicg.github.io/ua-client-hints/#dictdef-ualowentropyjson
interface UALowEntropyJSON {
readonly brands: NavigatorUABrandVersion[];
readonly mobile: boolean;
readonly platform: string;
}

// https://wicg.github.io/ua-client-hints/#navigatoruadata
interface NavigatorUAData extends UALowEntropyJSON {
getHighEntropyValues(hints: string[]): Promise<UADataValues>;
toJSON(): UALowEntropyJSON;
}