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

Fix CI, default to live over msal flow #97

Merged
merged 3 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:

strategy:
matrix:
node-version: [14.x]
node-version: [20.x]

steps:
- uses: actions/checkout@v2
Expand All @@ -22,4 +22,6 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
- run: npm install
- run: npm test
- run: npm test
env:
MSAL_CLIENT_ID: ${{ secrets.MSAL_CLIENT_ID }}
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# prismarine-auth
[![NPM version](https://img.shields.io/npm/v/prismarine-auth.svg)](http://npmjs.com/package/prismarine-auth)
[![Build Status](https://github.com/PrismarineJS/prismarine-auth/workflows/CI/badge.svg)](https://github.com/PrismarineJS/prismarine-auth/actions?query=workflow%3A%22CI%22)
[![Discord](https://img.shields.io/badge/chat-on%20discord-brightgreen.svg)](https://discord.gg/GsEFRM8)
[![Official Discord](https://img.shields.io/static/v1.svg?label=PrismarineJS&message=Discord&color=blue&logo=discord)](https://discord.gg/GsEFRM8)
[![Try it on gitpod](https://img.shields.io/badge/try-on%20gitpod-brightgreen.svg)](https://gitpod.io/#https://github.com/PrismarineJS/prismarine-auth)

Quickly and easily obtain auth tokens to authenticate with Microsoft/Xbox/Minecraft/Mojang
Expand All @@ -24,8 +24,6 @@ npm install prismarine-auth
- [deviceType] {string} - See the [API.md](docs/API.md)
- onMsaCode {Function} - (For device code auth) What we should do when we get the code. Useful for passing the code to another function.

[View more examples](https://github.com/PrismarineJS/prismarine-auth/tree/master/examples)

### Examples

### getMsaToken
Expand All @@ -39,6 +37,15 @@ const flow = new Authflow(userIdentifier, cacheDir)
flow.getMsaToken().then(console.log)
```

**Note**: By default, this library will authenticate as Minecraft for Nintendo Switch, with a `flow` set to `live`. For non-Minecraft applications you should
register for Microsoft Azure Oauth token. See https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app#register-an-application for more information on obtaining an Azure token. You then use it with the `msal` flow like this:

```js
const flow = new Authflow(userIdentifier, cacheDir, { flow: 'msal', authTitle: '000-000-000-000' })
```

If `flow` is `live`, the default, then you can only specify existing Microsoft client IDs. This library exposes some default Microsoft client IDs under the exported `Titles` object. See the [types](./index.d.ts) for more information.

### getXboxToken
See [docs/API.md](docs/API.md)

Expand Down Expand Up @@ -71,6 +78,11 @@ flow.getMinecraftJavaToken().then(console.log)
### getMinecraftBedrockToken
See [docs/API.md](docs/API.md) and [example](examples).

### More
[View more examples here](https://github.com/PrismarineJS/prismarine-auth/tree/master/examples).

See the [types](./index.d.ts) to checkout the full API.

## API

See [docs/API.md](docs/API.md)
Expand Down
2 changes: 2 additions & 0 deletions docs/API.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## prismarine-auth

See the [types](../index.d.ts) for additional information on the exposed API.

### Authflow

This is the main exposed class you interact with. Every instance holds its own token cache.
Expand Down
2 changes: 2 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ declare module 'prismarine-auth' {
}

export interface MicrosoftAuthFlowOptions {
// If using Azure auth, specify an custom object to pass to MSAL
msalConfig?: object
authTitle?: Titles
deviceType?: string
deviceVersion?: string
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"description": "Authentication library for Microsoft, Xbox Live, and Minecraft with caching support",
"main": "index.js",
"scripts": {
"test": "mocha --recursive --reporter spec --exit",
"mocha": "mocha test/*.test.js --reporter spec --exit",
"test": "npm run mocha",
"pretest": "npm run lint",
"lint": "standard",
"fix": "standard --fix"
Expand Down
10 changes: 8 additions & 2 deletions src/MicrosoftAuthFlow.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const JavaTokenManager = require('./TokenManagers/MinecraftJavaTokenManager')
const XboxTokenManager = require('./TokenManagers/XboxTokenManager')
const MsaTokenManager = require('./TokenManagers/MsaTokenManager')
const BedrockTokenManager = require('./TokenManagers/MinecraftBedrockTokenManager')
const Titles = require('./common/Titles')

async function retry (methodFn, beforeRetry, times) {
while (times--) {
Expand All @@ -31,7 +32,7 @@ class MicrosoftAuthFlow {
if (options && !options.flow) {
throw new Error("Missing 'flow' argument in options. See docs for more information.")
}
this.options = options || { flow: 'msal' }
this.options = options || { flow: 'live', authTitle: Titles.MinecraftNintendoSwitch }
this.initTokenManagers(username, cache)
this.codeCallback = codeCallback
}
Expand Down Expand Up @@ -62,7 +63,12 @@ class MicrosoftAuthFlow {
this.msa = new LiveTokenManager(this.options.authTitle, ['service::user.auth.xboxlive.com::MBI_SSL'], cache({ cacheName: this.options.flow, username }))
this.doTitleAuth = true
} else if (this.options.flow === 'msal') {
const config = Object.assign({ ...msalConfig }, this.options.authTitle ? { auth: { ...msalConfig.auth, clientId: this.options.authTitle } } : {})
let config = this.options.msalConfig
if (!config) {
config = structuredClone(msalConfig)
if (!this.options.authTitle) throw new Error('Must specify an Azure client ID token inside the `authTitle` parameter when using Azure-based auth. See https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app#register-an-application for more information on obtaining an Azure token.')
config.auth.clientId = this.options.authTitle
}
this.msa = new MsaTokenManager(config, ['XboxLive.signin', 'offline_access'], cache({ cacheName: 'msal', username }))
} else {
throw new Error(`Unknown flow: ${this.options.flow} (expected "live", "sisu", or "msal")`)
Expand Down
2 changes: 1 addition & 1 deletion src/TokenManagers/MsaTokenManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ class MsaTokenManager {
resolve(response)
})
}).catch((error) => {
console.warn('[msa] Error getting device code')
console.warn('[msa] Error getting device code. Ensure your supplied `authTitle` token (or clientId in your supplied MSAL config) is valid and that it has permission to do non-interactive code based auth.')
console.debug(JSON.stringify(error))
reject(error)
})
Expand Down
2 changes: 1 addition & 1 deletion src/common/Constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module.exports = {
},
msalConfig: {
// Initialize msal
// Docs: https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-common/docs/request.md#public-apis-1
// Docs: https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-node/docs/configuration.md#usage
auth: {
// the minecraft client:
// clientId: "000000004C12AE6F",
Expand Down
File renamed without changes.
25 changes: 15 additions & 10 deletions test/devicecode.js → test/devicecode.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,20 @@ const { expect } = chai
const crypto = require('crypto')
const curve = 'secp384r1'

describe('device code authentication', () => {
it('should give us a token', (done) => {
const onMsaCode = (code) => {
if (!code) done(Error('missing user code'))
if (code.userCode) done()
}
const flow = new Authflow('[email protected]', './test', null, onMsaCode)
flow.getXboxToken()
})
describe('device code authentication', function () {
this.timeout(4000)
if (process.env.MSAL_CLIENT_ID) {
// Non-MSAL will be tested in Minecraft Bedrock condition below
it('should give us a token (MSAL)', (done) => {
const onMsaCode = (code) => {
if (!code) done(Error('missing user code'))
if (code.userCode) done()
}
const flow = new Authflow('[email protected]', './test', { flow: 'msal', authTitle: process.env.MSAL_CLIENT_ID }, onMsaCode)
flow.getXboxToken().catch(done)
})
}

it('should error if no certificate is present for bedrock', async () => {
const flow = new Authflow('testauthflow', './test', { authTitle: Titles.MinecraftNintendoSwitch, flow: 'live' })
await expect(flow.getMinecraftBedrockToken()).to.eventually.be.rejectedWith('Need to specifiy a ECDH x509 URL encoded public key')
Expand All @@ -30,6 +35,6 @@ describe('device code authentication', () => {
const keypair = crypto.generateKeyPairSync('ec', { namedCurve: curve })
const clientX509 = keypair.toString('base64')
const flow = new Authflow('username', './test', { authTitle: Titles.MinecraftNintendoSwitch, flow: 'live' }, onMsaCode)
flow.getMinecraftBedrockToken(clientX509)
flow.getMinecraftBedrockToken(clientX509).catch(done)
})
})
4 changes: 2 additions & 2 deletions test/password.js → test/password.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ const chaiAsPromised = require('chai-as-promised')
chai.use(chaiAsPromised)
const { expect } = chai

const { Authflow } = require('../')
const { Authflow, Titles } = require('prismarine-auth')

describe('password authentication', async () => {
it('should fail if not given a valid password', async () => {
const flow = new Authflow('[email protected]', './test', { password: 'sdfasdfas', flow: 'msal' })
const flow = new Authflow('[email protected]', './test', { password: 'sdfasdfas', flow: 'live', authTitle: Titles.MinecraftJava })
await expect(flow.getXboxToken()).to.eventually.be.rejectedWith('Invalid credentials')
})
})
Loading