Bump oauth2-pkce from ^2.1.3 to ^3.0.0. In v3, getTokens() throws ErrorAccessTokenExpired when the token has expired and no onAccessTokenExpiry handler is configured. Add the handler to the OSM auth client config so it automatically refreshes via the refresh token instead of throwing
120 lines
3.2 KiB
JavaScript
120 lines
3.2 KiB
JavaScript
import Service from '@ember/service';
|
|
import { tracked } from '@glimmer/tracking';
|
|
import { OAuth2AuthCodePkceClient } from 'oauth2-pkce';
|
|
|
|
class MarcoOsmAuthStorage {
|
|
saveState(serializedState) {
|
|
localStorage.setItem('marco:osm_auth_state', serializedState);
|
|
}
|
|
loadState() {
|
|
const state = localStorage.getItem('marco:osm_auth_state');
|
|
if (!state) return false;
|
|
try {
|
|
JSON.parse(state);
|
|
return state;
|
|
} catch (e) {
|
|
console.warn('Failed to parse OSM auth state', e);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
export default class OsmAuthService extends Service {
|
|
@tracked isConnected = false;
|
|
@tracked userDisplayName = null;
|
|
|
|
oauthClient;
|
|
|
|
constructor() {
|
|
super(...arguments);
|
|
|
|
const clientId =
|
|
import.meta.env.VITE_OSM_CLIENT_ID || 'YOUR_CLIENT_ID_HERE';
|
|
const oauthUrl =
|
|
import.meta.env.VITE_OSM_OAUTH_URL || 'https://www.openstreetmap.org';
|
|
|
|
const redirectUrl =
|
|
import.meta.env.VITE_OSM_REDIRECT_URI ||
|
|
`${window.location.origin}/oauth/osm/callback`;
|
|
|
|
this.oauthClient = new OAuth2AuthCodePkceClient(
|
|
{
|
|
scopes: ['read_prefs', 'write_api'],
|
|
authorizationUrl: `${oauthUrl}/oauth2/authorize`,
|
|
tokenUrl: `${oauthUrl}/oauth2/token`,
|
|
clientId: clientId,
|
|
redirectUrl: redirectUrl,
|
|
storeRefreshToken: true,
|
|
onAccessTokenExpiry() {
|
|
return this.exchangeRefreshTokenForAccessToken();
|
|
},
|
|
},
|
|
new MarcoOsmAuthStorage()
|
|
);
|
|
|
|
this.restoreSession();
|
|
}
|
|
|
|
async restoreSession() {
|
|
try {
|
|
await this.oauthClient.ready;
|
|
} catch (e) {
|
|
console.warn('oauthClient.ready failed', e);
|
|
}
|
|
const isAuthorized = await this.oauthClient.isAuthorized();
|
|
if (isAuthorized) {
|
|
this.isConnected = true;
|
|
const storedName = localStorage.getItem('marco:osm_user_display_name');
|
|
if (storedName) {
|
|
this.userDisplayName = storedName;
|
|
} else {
|
|
await this.fetchUserInfo();
|
|
}
|
|
}
|
|
}
|
|
|
|
async login() {
|
|
await this.oauthClient.requestAuthorizationCode();
|
|
}
|
|
|
|
async handleCallback() {
|
|
await this.oauthClient.receiveCode();
|
|
await this.oauthClient.getTokens();
|
|
this.isConnected = true;
|
|
await this.fetchUserInfo();
|
|
}
|
|
|
|
async logout() {
|
|
await this.oauthClient.reset();
|
|
this.isConnected = false;
|
|
this.userDisplayName = null;
|
|
localStorage.removeItem('marco:osm_user_display_name');
|
|
}
|
|
|
|
async fetchUserInfo() {
|
|
try {
|
|
const tokens = await this.oauthClient.getTokens();
|
|
// eslint-disable-next-line warp-drive/no-external-request-patterns
|
|
const response = await fetch(
|
|
'https://api.openstreetmap.org/api/0.6/user/details.json',
|
|
{
|
|
headers: {
|
|
Authorization: `Bearer ${tokens.accessToken}`,
|
|
},
|
|
}
|
|
);
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
console.debug('OSM data:', data);
|
|
const displayName = data.user.display_name;
|
|
this.userDisplayName = displayName;
|
|
localStorage.setItem('marco:osm_user_display_name', displayName);
|
|
} else {
|
|
console.error('Failed to fetch OSM user info', response.status);
|
|
}
|
|
} catch (e) {
|
|
console.error('Error fetching OSM user info', e);
|
|
}
|
|
}
|
|
}
|