OAuth in Obsidian Plugins, Part 2
Previously I wrote a blog post as I was exploring how to use OAuth for an Obsidian plugin. At the time, I just had gotten a simple flow to obtain the access token and make API calls.
That was all I had accomplished before I left for a vacation. I came back and put more time into it. The Trakt for Obsidian plugin is going to help me keep track of all the TV shows that I watch.
If you want to set it up, you first need to select the “Connect” button. That will trigger the browser opening to authorize the app.
if (this.settings.refresh) {
new Setting(containerEl)
.setName(‘Connected to Trakt’)
.addButton((component) => {
component.setButtonText(‘Remove Authorization’)
component.onClick(async () => {
delete this.settings.refresh
await this.plugin.saveSettings(this.settings)
new Notice(‘Logged out of Trakt account’)
})
})
} else {
new Setting(containerEl)
.setName(‘Connect to Trakt account’)
.addButton((component) => {
component.setButtonText(‘Connect’)
component.onClick(() => {
const traktAuthUrl = this.plugin.trakt.get_url()
window.location.href = traktAuthUrl
})
})
}
But technically this is only true if you are unauthorized. If you have been authorized before, and there is still a refresh token saved, then you don’t need to reconnect. Instead I want to give you the option to disconnect.
This isn’t necessarily straightforward, since I need to use a protocol handler to receive events after the OAuth flow returns.
this.registerObsidianProtocolHandler(‘trakt’, async (data) => {
const {code, state} = data
await this.trakt.exchange_code(code, state)
this.settings.refresh = JSON.stringify(this.trakt.export_token())
await this.saveSettings(this.settings)
new Notice(‘You are now connected to Trakt’)
})
So I can’t directly refresh the Settings dialog. But if I don’t, Obsidian comes back to the dialog and doesn’t indicate that anything has changed. How can I refresh the Settings dialog when the data changes?
Well, I don’t know. At least, not directly. But as a compromise I figured I could easily use an interval that runs for as long as the OAuth flow takes, until the plugin has a refresh token. It keeps running the setting’s display
method four times a second.
Once it finds a refresh token, then it clears the interval and changes the button that is shown.
It’s not quite the most optimal solution, but the whole process is lightweight and short enough that I don’t expect to see any performance issues.
if (this.settings.refresh) {
clearInterval(this.displayInterval as number)
new Setting(containerEl)
.setName(‘Connected to Trakt’)
.addButton((component) => {
component.setButtonText(‘Remove
component.onClick(async () => {
delete this.settings.refresh
await this.plugin.saveSettings(this.settings)
new Notice(‘Logged out of Trakt account’)
this.display() // Reload
})
})
} else {
new Setting(containerEl)
.setName(‘Connect to Trakt account’)
.addButton((component) => {
component.setButtonText(‘Connect’)
component.onClick(() => {
const traktAuthUrl = this.plugin.trakt.get_url()
window.location.href = traktAuthUrl
this.displayInterval = setInterval(() => {
this.display()
}, 250)
})
})
}
This means I need to define a field of my plugin settings to store my interval. I had trouble figuring out the type to use, since the TypeScript compiler wanted it to be NodeJS.Timeout
when the browser expects it to be number
. I just decided it should be unknown
and then do casting since the whole thing was self-contained.
I wouldn’t necessarily recommend it for a larger application though. I’d probably create some kind of alias like type MyTimeout = number | NodeJS.Timeout
or run a cast when setInterval
is called.
class TraktSettingTab extends PluginSettingTab {
plugin: TraktPlugin;
settings: any
displayInterval?: unknown
// …
}
This is just first iteration of my plugin, able to run but without a lot of bells and whistles. The source code is all available on GitHub if you want to put some time into improving it. The community around Obsidian plugins is really vibrant and has more expertise than I do. But I’m learning a lot.
Or you can install the Trakt for Obsidian plugin in your app right now.