Skip to content

Commit

Permalink
feat(auth): add a default deviceName when remembering device
Browse files Browse the repository at this point in the history
  • Loading branch information
Ashwin Kumar committed May 1, 2024
1 parent d218d81 commit 1485390
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 2 deletions.
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;
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
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;

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
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;
}

0 comments on commit 1485390

Please sign in to comment.