This repository demonstrates how a Node.js and Express website can integrate with the DRM-X 5.0 license service to protect encrypted video, audio, documents, and LMS course content.
The example receives license parameters posted automatically by DRM-X and the ZJGet browser, restores the website login Session, validates course access, calls the DRM-X 5.0 SOAP service, and returns the license HTML to ZJGet.
This is an integration example, not a complete LMS or production user system. Replace the fixed login and purchased-course list with your own authentication, order, enrollment, subscription, or LMS data.
- Express 5 application using CommonJS and
async/await. - Fixed test account with login state stored in
express-session. - Existing login Session is reused when the user opens encrypted content.
- A license request received before login is saved in the Session and resumed after login.
- Same-origin POST relay for cross-site requests whose first POST does not include the Session cookie.
- Nine ZJGet POST parameters are read from the encrypted content rather than entered manually.
yourproductidis validated against the courses purchased by the logged-in user.- Multiple course IDs are supported using hyphens, such as
1001-1002-1003. - The first listed course that the current user owns is selected; this does not grant access to the other courses.
rightsidposted by ZJGet is used for both the permission update and license request.- Existing DRM-X users are checked for revocation and lockout.
- DRM-X users are created automatically when they do not exist.
- Permission dates and device binding count are editable in one configuration file.
- China and international DRM-X 5.0 service URLs are supported.
DRM-X5_NodeJS+Express_Integration_Example/
├─ config.js # DRM-X account, demo user, course, and date settings
├─ DRMXService.js # Express routes and complete DRM-X license workflow
├─ package.json
├─ package-lock.json
└─ views/
├─ layout.hbs
├─ login.hbs
├─ relay.hbs
├─ licstore.hbs
└─ licerror.hbs
- Node.js 20 or later is recommended.
- A DRM-X 5.0 account with website integration enabled.
- A DRM-X 5.0 License Profile and Rights ID.
- ZJGet browser and an encrypted file for end-to-end testing.
cd "DRM-X5_NodeJS+Express_Integration_Example"
npm installOpen config.js and set the following values:
DRMX5_ADMIN_EMAIL: 'YOUR_ADMIN_EMAIL',
DRMX5_WEB_SERVICE_AUTH_STR: 'YOUR_WEB_SERVICE_AUTH_STR',
DRMX5_GROUP_ID: 'YOUR_GROUP_ID',Select the service address for the customer's DRM-X account. Do not add ?wsdl; the code adds it only when loading the service definition.
// International service
DRMX5_SERVICE_URL: 'https://5.drm-x.com/haihaisoftlicenseservice.asmx',
// China service
DRMX5_SERVICE_URL: 'https://5.drm-x.cn/haihaisoftlicenseservice.asmx',The example uses a fixed login account:
DEMO_USERNAME: 'student',
DEMO_PASSWORD: '123456',
DEMO_EMAIL: 'student@example.com',
DEMO_FULL_NAME: 'Demo Student',Configure the course IDs purchased by this test user:
DEMO_PURCHASED_COURSE_IDS: ['1001'],Configure the permission validity period and new-user binding count:
DEMO_COURSE_BEGIN_DATE: '2026-01-01',
DEMO_COURSE_EXPIRATION_DATE: '2036-01-01',
DEMO_BIND_NUMBER: '1',UpdateRightWithDisableVirtualMachine uses these course dates. ExpirationAfterFirstUse is fixed at -1, so the absolute expiration date controls validity.
npm startOpen the test login page:
http://localhost:3000/login
The DRM-X 5.0 website integration URL is:
http://localhost:3000/licstore5
For a real deployment, use an HTTPS public URL. Do not configure /login, /index, or the site root as the license integration URL.
DRM-X reads the current License Profile and encrypted content, then ZJGet automatically posts the following fields to /licstore5.
| POST field | Meaning | How the example uses it |
|---|---|---|
profileid |
Current DRM-X 5.0 License Profile ID | Sent as ProfileID when requesting the license |
clientinfo |
Client license data generated by ZJGet | Sent unchanged as ClientInfo |
rightsid |
Rights ID selected in the License Profile | Used by both permission update and license request |
yourproductid |
Product ID stored in the License Profile | Treated as one or more course IDs and validated against purchased courses |
platform |
ZJGet platform information | Sent as Platform |
contenttype |
Protected content type | Sent as ContentType |
version |
ZJGet client version | Sent as Version |
return_url |
Address or identifier used after license completion | Used by the result page after the license is obtained |
mac |
Device or client binding information | Sent as Mac |
The web page does not need a manual form for these values. In normal use, DRM-X and ZJGet obtain them from the encrypted file and License Profile.
yourproductid must come from the ZJGet POST request. It is not a fixed course ID in the frontend or license endpoint.
If a License Profile contains multiple course IDs, separate them with hyphens:
1001-1002-1003
The code checks the values in POST order and selects the first ID also present in DEMO_PURCHASED_COURSE_IDS.
Examples when the user owns courses 1001 and 1003:
Posted yourproductid |
Result |
|---|---|
1001 |
Allowed; course 1001 matches |
1001-1002 |
Allowed; course 1001 matches first |
1002-1003-1001 |
Allowed; course 1003 matches first |
1002-1004 |
Rejected with HTTP 403 |
Passing this check authorizes only the current license request. It does not give the user permission to view courses they did not purchase.
ZJGet POST /licstore5
│
├─ Same-origin POST relay establishes/restores the Session cookie
├─ Validate and save the nine POST parameters
├─ Check website login Session
│ └─ Not logged in: show /login and resume after login
├─ Validate POST yourproductid against purchased courses
├─ CheckUserExists
│ └─ User does not exist: AddNewUser
├─ Existing user: CheckUserIsRevoked
├─ Existing user: GetUserDetails and read IsLockedOut
├─ UpdateRightWithDisableVirtualMachine using POST rightsid
└─ getLicenseRemoteToTableWithVersionWithMac using the same rightsid
All calls are awaited in sequence. The license request does not begin until the required preceding operation succeeds.
Checks whether the authenticated website username already exists in the DRM-X account. False causes the example to call AddNewUser; unexpected values stop the workflow.
Creates the DRM-X user and assigns the configured Group ID and binding count. The result must equal 1.
Runs only for an existing DRM-X user. A revoked user receives HTTP 403 and cannot obtain a license.
Reads IsLockedOut for an existing user. A locked user receives HTTP 403. The code also handles the field when it is nested inside the SOAP result object.
Updates the Rights ID posted in rightsid. The example sets unlimited play count, absolute course dates, ExpirationAfterFirstUse=-1, watermark settings, blacklist/whitelist settings, and virtual-machine restrictions. The result must equal 1.
Requests the final license using the same posted Rights ID and the posted device information. A successful result must contain the license_div_drm-x5 marker.
| Route | Method | Purpose |
|---|---|---|
/ |
GET | Redirects to the login page |
/login |
GET/POST | Displays the test login, creates the Session, and resumes a pending license request |
/logout |
POST | Clears the current Session |
/api/session |
GET | Returns login status and whether a pending license request exists |
/licstore5 |
POST | Main DRM-X/ZJGet integration endpoint |
/licstore5?resume=1 |
GET | Resumes the saved license request after login |
/index |
POST | Backward-compatible alias for the original sample; do not use for new configuration |
- Replace the fixed account with the customer's database, SSO, or existing authentication system.
- Replace
DEMO_PURCHASED_COURSE_IDSwith an order, enrollment, subscription, or LMS permission query for the current authenticated user. - Keep the website-to-DRM-X username mapping stable and unique.
- Use HTTPS and set the Session cookie
secureoption totrue. - Replace the default in-memory Session store with Redis, a database, or another shared store.
- Change
SESSION_SECRETand protect all DRM-X credentials. - If the application is behind a proxy, configure trusted proxies before reading forwarded client IP headers.
- Decide whether every license request should update a shared Rights ID according to the customer's permission policy.
- Do not expose SOAP credentials, raw internal errors, or DRM-X authentication strings to the browser.
This is expected when the URL is opened directly. Open an encrypted file with ZJGet so it can POST the license parameters.
Check that cookies are enabled, the browser uses the same hostname throughout the test, and the Session store is working. Do not switch between localhost and 127.0.0.1 during one test.
Replace the YOUR_... placeholders with the DRM-X administrator email, Web Service authentication string, and Group ID.
Verify the China/international service URL, TLS/network access, credentials, and timeout. The configured service address must end in haihaisoftlicenseservice.asmx without a duplicate ?wsdl.
The user does not own any posted course ID, or the DRM-X user is revoked or locked. Check the course list, License Profile Product ID, and DRM-X user status.
Change PORT in config.js, then update the DRM-X integration URL to use the same port.