Skip to content

Commit

Permalink
Merge pull request #715 from Shopify/liz/store-user-info
Browse files Browse the repository at this point in the history
Update Prisma Adapter to Store User Info for Online Tokens
  • Loading branch information
lizkenyon committed May 10, 2024
2 parents 0e791d3 + 31b3062 commit ec4270a
Show file tree
Hide file tree
Showing 8 changed files with 331 additions and 38 deletions.
52 changes: 52 additions & 0 deletions .changeset/rich-panthers-refuse.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
"@shopify/shopify-app-session-storage-prisma": major
"@shopify/shopify-app-session-storage-test-utils": patch
---

## Store user information as part of the session

With this change when using online access tokens, the user information is stored as part of the session. Previously only the user ID was stored. This will enable changing of page content or limiting of page visibility by user, as well as unlock logging users actions. This is a breaking change, as the Prisma schema has been updated to include the user information.

For more information review the [migration guide](../packages/apps/session-storage/shopify-app-session-storage-prisma/MIGRATION_V5.md).


<details>
The new session will include the following data:

```ts
{
id: 'online_session_id',
shop: 'online-session-shop',
state: 'online-session-state',
isOnline: true,
scope: 'online-session-scope',
accessToken: 'online-session-token',
expires: 2022-01-01T05:00:00.000Z,
onlineAccessInfo: {
associated_user: {
id: 1,
first_name: 'online-session-first-name'
last_name: 'online-session-last-name',
email: 'online-session-email',
locale: 'online-session-locale',
email_verified: false,
account_owner: true,
collaborator: false,
},
}
}
```

You will be able to access the user information on the Session object:

```ts
const { admin, session } = await authenticate.admin(request);

console.log("user id", session.onlineAccessInfo.associated_user.id);
console.log("user email", session.onlineAccessInfo.associated_user.email);
console.log("account owner", session.onlineAccessInfo.associated_user.account_owner);
```

</details>


Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Migrating to `v.5.0.0`

Version `5.0.0` of the `@shopify/shopify-app-session-storage-prisma` introduces a breaking change to the session storage schema. The user information is now stored as part of the session. Previously only the user ID was stored. This change requires updating the Prisma schema to include the user information.

## Updating the Prisma schema

Update the `Session` model in the Prisma schema to include the user information:

```prisma
model Session {
id String @id
shop String
state String
isOnline Boolean @default(false)
scope String?
expires DateTime?
accessToken String
userId BigInt?
// New fields
firstName String?
lastName String?
email String?
accountOwner Boolean @default(false)
locale String?
collaborator Boolean? @default(false)
emailVerified Boolean? @default(false)
}
```

Run the following prisma commands to update the database schema:

```sh
npx prisma migrate dev
```

### Update Types

Update the generated types to include the new fields:

```ts
npx prisma generate
```

## Using the user information

The user information will now be available on the `Session` object:

```ts
const {admin, session} = await authenticate.admin(request);

console.log('user id', session.onlineAccessInfo.associated_user.id);
console.log('user email', session.onlineAccessInfo.associated_user.email);
console.log(
'account owner',
session.onlineAccessInfo.associated_user.account_owner,
);
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
-- RedefineTables
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_Session" (
"id" TEXT NOT NULL PRIMARY KEY,
"shop" TEXT NOT NULL,
"state" TEXT NOT NULL,
"isOnline" BOOLEAN NOT NULL DEFAULT false,
"scope" TEXT,
"expires" DATETIME,
"accessToken" TEXT NOT NULL,
"userId" BIGINT,
"firstName" TEXT,
"lastName" TEXT,
"email" TEXT,
"accountOwner" BOOLEAN,
"locale" TEXT,
"collaborator" BOOLEAN,
"emailVerified" BOOLEAN
);
INSERT INTO "new_Session" ("accessToken", "expires", "id", "isOnline", "scope", "shop", "state", "userId") SELECT "accessToken", "expires", "id", "isOnline", "scope", "shop", "state", "userId" FROM "Session";
DROP TABLE "Session";
ALTER TABLE "new_Session" RENAME TO "Session";
PRAGMA foreign_key_check;
PRAGMA foreign_keys=ON;
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
-- RedefineTables
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_MySession" (
"id" TEXT NOT NULL PRIMARY KEY,
"shop" TEXT NOT NULL,
"state" TEXT NOT NULL,
"isOnline" BOOLEAN NOT NULL DEFAULT false,
"scope" TEXT,
"expires" DATETIME,
"accessToken" TEXT NOT NULL,
"userId" BIGINT,
"firstName" TEXT,
"lastName" TEXT,
"email" TEXT,
"accountOwner" BOOLEAN,
"locale" TEXT,
"collaborator" BOOLEAN,
"emailVerified" BOOLEAN
);
INSERT INTO "new_MySession" ("accessToken", "expires", "id", "isOnline", "scope", "shop", "state", "userId") SELECT "accessToken", "expires", "id", "isOnline", "scope", "shop", "state", "userId" FROM "MySession";
DROP TABLE "MySession";
ALTER TABLE "new_MySession" RENAME TO "MySession";
PRAGMA foreign_key_check;
PRAGMA foreign_keys=ON;
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,38 @@ datasource db {
}

model Session {
id String @id
shop String
state String
isOnline Boolean @default(false)
scope String?
expires DateTime?
accessToken String
userId BigInt?
id String @id
shop String
state String
isOnline Boolean @default(false)
scope String?
expires DateTime?
accessToken String
userId BigInt?
firstName String?
lastName String?
email String?
accountOwner Boolean?
locale String?
collaborator Boolean?
emailVerified Boolean?
}

// We use this in unit tests to ensure it works with a different model name
model MySession {
id String @id
shop String
state String
isOnline Boolean @default(false)
scope String?
expires DateTime?
accessToken String
userId BigInt?
id String @id
shop String
state String
isOnline Boolean @default(false)
scope String?
expires DateTime?
accessToken String
userId BigInt?
firstName String?
lastName String?
email String?
accountOwner Boolean?
locale String?
collaborator Boolean?
emailVerified Boolean?
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,16 @@ describe('PrismaSessionStorage', () => {
});

// Using the default table name
batteryOfTests(async () => new PrismaSessionStorage<PrismaClient>(prisma));
batteryOfTests(
async () => new PrismaSessionStorage<PrismaClient>(prisma),
true,
);

// Using a custom table name
batteryOfTests(
async () =>
new PrismaSessionStorage<PrismaClient>(prisma, {tableName: 'mySession'}),
true,
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,18 @@ export class PrismaSessionStorage<T extends PrismaClient>
userId:
(sessionParams.onlineAccessInfo?.associated_user
.id as unknown as bigint) || null,
firstName:
sessionParams.onlineAccessInfo?.associated_user.first_name || null,
lastName:
sessionParams.onlineAccessInfo?.associated_user.last_name || null,
email: sessionParams.onlineAccessInfo?.associated_user.email || null,
accountOwner:
sessionParams.onlineAccessInfo?.associated_user.account_owner || false,
locale: sessionParams.onlineAccessInfo?.associated_user.locale || null,
collaborator:
sessionParams.onlineAccessInfo?.associated_user.collaborator || false,
emailVerified:
sessionParams.onlineAccessInfo?.associated_user.email_verified || false,
};
}

Expand All @@ -137,8 +149,25 @@ export class PrismaSessionStorage<T extends PrismaClient>
shop: row.shop,
state: row.state,
isOnline: row.isOnline,
userId: String(row.userId),
firstName: String(row.firstName),
lastName: String(row.lastName),
email: String(row.email),
locale: String(row.locale),
};

if (row.accountOwner !== null) {
sessionParams.accountOwner = row.accountOwner;
}

if (row.collaborator !== null) {
sessionParams.collaborator = row.collaborator;
}

if (row.emailVerified !== null) {
sessionParams.emailVerified = row.emailVerified;
}

if (row.expires) {
sessionParams.expires = row.expires.getTime();
}
Expand All @@ -151,11 +180,7 @@ export class PrismaSessionStorage<T extends PrismaClient>
sessionParams.accessToken = row.accessToken;
}

if (row.userId) {
sessionParams.onlineAccessInfo = String(row.userId);
}

return Session.fromPropertyArray(Object.entries(sessionParams));
return Session.fromPropertyArray(Object.entries(sessionParams), true);
}

private getSessionTable(): T['session'] {
Expand Down

0 comments on commit ec4270a

Please sign in to comment.