Skip to content

Commit

Permalink
Add a forceRefresh option to Authflow (#100)
Browse files Browse the repository at this point in the history
* Add a forceRefresh option

* Update index.d.ts

* Update basic.js

* add a lint error

* Fix linting errors

* add a lint error

* Update basic.js

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
extremeheat and github-actions[bot] committed May 26, 2024
1 parent b84c345 commit 01c8266
Show file tree
Hide file tree
Showing 6 changed files with 30 additions and 25 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ npm install prismarine-auth
- cacheDirectory? {String | Function} - Where we will store your tokens (optional) or a factory function that returns a cache.
- options {Object?}
- [flow] {enum} Required if options is specified - see [API.md](docs/API.md) for options
- [forceRefresh] {boolean} - Clear all cached tokens for the specified `username` to get new ones on subsequent token requests
- [password] {string} - If passed we will do password based authentication.
- [authTitle] {string} - See the [API.md](docs/API.md)
- [deviceType] {string} - See the [API.md](docs/API.md)
Expand Down
4 changes: 4 additions & 0 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ This is the main exposed class you interact with. Every instance holds its own t
* `password` (optional) If you specify this option, we use password based auth. Note this may be unreliable.
* `authTitle` - The client ID for the service you are logging into. When using the `msal` flow, this is your custom Azure client token. When using `live`, this is the Windows Live SSO client ID - used when authenticating as a Windows app (such as a vanilla Minecraft client). For a list of titles, see `require('prismarine-auth').Titles` and FAQ section below for more info. (Required if using `sisu` or `live` flow, on `msal` flow we fallback to a default client ID.)
* `deviceType` (optional) if specifying an authTitle, the device type to auth as. For example, `Win32`, `iOS`, `Android`, `Nintendo`
* `forceRefresh` (optional) boolean - Clear all cached tokens for the specified `username` to get new ones on subsequent token requests
* `codeCallback` (optional) The callback to call when doing device code auth. Otherwise, the code will be logged to the console.

#### getMsaToken () : Promise<string>
Expand Down Expand Up @@ -94,6 +95,9 @@ As an example of usage, you could create a minimal in memory cache like this (no
```js
class InMemoryCache {
private cache = {}
async reset () {
// (should clear the data in the cache like a first run)
}
async getCached () {
return this.cache
}
Expand Down
3 changes: 2 additions & 1 deletion examples/xbox/basic.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const { Authflow } = require('prismarine-auth')
const flow = new Authflow() // No parameters needed
// No parameters needed - will login as Minecraft by default unless a custom Azure client ID is passed (see ./azure.js)
const flow = new Authflow()
module.exports = flow.getXboxToken().then(console.log)
7 changes: 3 additions & 4 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,6 @@ declare module 'prismarine-auth' {
* @param codeCallback Optional callback to recieve token information using device code auth
*/
constructor(username?: string, cache?: string | CacheFactory, options?: MicrosoftAuthFlowOptions, codeCallback?: (res: ServerDeviceCodeResponse) => void)
/**
* Deletes the caches in the specified cache directory.
*/
static resetTokenCaches(cache: string): boolean

// Returns a Microsoft Oauth access token -- https://docs.microsoft.com/en-us/azure/active-directory/develop/access-tokens
getMsaToken(): Promise<string>
Expand Down Expand Up @@ -101,6 +97,8 @@ declare module 'prismarine-auth' {
deviceVersion?: string
password?: string
flow: 'live' | 'msal' | 'sisu'
// Reset the cache and obtain fresh tokens for everything
forceRefresh?: boolean
}

export enum Titles {
Expand Down Expand Up @@ -130,6 +128,7 @@ declare module 'prismarine-auth' {
}

export interface Cache {
reset(): Promise<void>
getCached(): Promise<any>
setCached(value: any): Promise<void>
setCachedPartial(value: any): Promise<void>
Expand Down
30 changes: 13 additions & 17 deletions src/MicrosoftAuthFlow.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,20 @@ async function retry (methodFn, beforeRetry, times) {
}
}

const CACHE_IDS = ['msal', 'live', 'sisu', 'xbl', 'bed', 'mca']

class MicrosoftAuthFlow {
constructor (username = '', cache = __dirname, options, codeCallback) {
this.username = username
if (options && !options.flow) {
throw new Error("Missing 'flow' argument in options. See docs for more information.")
}
this.options = options || { flow: 'live', authTitle: Titles.MinecraftNintendoSwitch }
this.initTokenManagers(username, cache)
this.initTokenManagers(username, cache, options?.forceRefresh)
this.codeCallback = codeCallback
}

initTokenManagers (username, cache) {
initTokenManagers (username, cache, forceRefresh) {
if (typeof cache !== 'function') {
let cachePath = cache

Expand All @@ -48,13 +50,20 @@ class MicrosoftAuthFlow {
fs.mkdirSync(cachePath, { recursive: true })
}
} catch (e) {
console.log('Failed to open cache dir', e)
console.log('Failed to open cache dir', e, ' ... will use current dir')
cachePath = __dirname
}

cache = ({ cacheName, username }) => {
if (!CACHE_IDS.includes(cacheName)) {
throw new Error(`Cannot instantiate cache for unknown ID: '${cacheName}'`)
}
const hash = createHash(username)
return new FileCache(path.join(cachePath, `./${hash}_${cacheName}-cache.json`))
const result = new FileCache(path.join(cachePath, `./${hash}_${cacheName}-cache.json`))
if (forceRefresh) {
result.reset()
}
return result
}
}

Expand All @@ -80,19 +89,6 @@ class MicrosoftAuthFlow {
this.mca = new JavaTokenManager(cache({ cacheName: 'mca', username }))
}

static resetTokenCaches (cache) {
if (!cache) throw new Error('You must provide a cache directory to reset.')
try {
if (fs.existsSync(cache)) {
fs.rmSync(cache, { recursive: true })
return true
}
} catch (e) {
console.log('Failed to clear cache dir', e)
return false
}
}

async getMsaToken () {
if (await this.msa.verifyTokens()) {
debug('[msa] Using existing tokens')
Expand Down
10 changes: 7 additions & 3 deletions src/common/cache/FileCache.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ class FileCache {
this.cacheLocation = cacheLocation
}

async reset () {
const cached = {}
fs.writeFileSync(this.cacheLocation, JSON.stringify(cached))
return cached
}

async loadInitialValue () {
try {
return JSON.parse(fs.readFileSync(this.cacheLocation, 'utf8'))
} catch (e) {
const cached = {}
fs.writeFileSync(this.cacheLocation, JSON.stringify(cached))
return cached
return this.reset()
}
}

Expand Down

0 comments on commit 01c8266

Please sign in to comment.