oauth2 API calls and refreshing tokens (chaining observables?)


#1

I’m writing an OAuth2 API client. Before making an API call I want to check the age of the access token and, if expired, get a new token with a refresh token. So, some code:

page calls something like (WAC is api client):

this.WAC.call('getDailyData', { date: '2016-02-01', gid: 3824 }) .subscribe( response => { console.log("DO STUFF WITH RESPONSE"); }, err => { console.log("FAILED TO GET DAILY DATA: ", err); } );

wac.ts

call(method, params) { ... return this.http.post(url, body, options) .map(res => res.json()); }

The above works just great if the access_token is still valid. However, if it’s no longer valid I need to make another http request to refresh the token. What’s the best way to do this? Is there a way to “interrupt” the process, go refresh the token, and then continue with the call() method to make the API call. Or do I need to return the observable from the refresh_token request and check the response on the subscribe method to determine if the token was just refreshed so then I can re-request the API call? I hope that makes sense.

Thanks,
Brian


#2

Here’s what works, but seems stupid:

    this.WAC.call(method, params) // try to make API call
        .subscribe(
            response => {
                if (response.access_token) { // if the response is new token data, we just refreshed, so call the original API method again
                    this.WAC.setAuthData(response);
                    this.WAC.call(method, params).subscribe(
                        response => {
                            this.displayWods(response);
                        }
                    );
                } else { // we got actual data back, not just token data from a refresh
                    this.displayWods(response);
                }
            },
            err => {
                console.log("FAILED TO GET DAILY DATA: ", err);
            }
        );

Inside WAC I have:

call(method, params) {
	// check if access_token is expired, if so use refresh token to get a new one
	let now = Date.now() / 1000;
	if (this.authData.access_expires < now) {
		console.log("GOTTA REFRESH!!!!!!");
		return this.refreshToken();
	} else {
		...
		
		return this.http.post(url, body, options)
			.map(res => res.json());
	}
}

refreshToken() {
	...
	
	return this.http.post('http://local.oapi.wodtogether.com/token.php', body, options)
		.map(res => res.json());
}

The problem here is that there’s going to be a lot of repeated logic for every API call in order to handle potential token refreshes. Instead of having to check the response for tokendata after each API call, is there a way to move all that logic inside of WAC (api client)?

Thanks!


#3

Does this StackOverflow topic look useful?


#4

Yes, I actually have that tab open as well! I just didn’t fully grasp it so I was hoping for another angle. But I’ll dig into it a little more and see what I can get. Thanks


#5

For the benefit of anyone else going through this, here’s what I ended up with (based on the SO topic linked above).

Page.ts – now nice and clean and straight forward

this.WAC.call(method, params)
	.subscribe(
		response => {
			// DO STUFF
		},
		err => {
			// ERROR
		}
	);

WAC.ts (API Client)

refreshToken() {
	...
	// call to get a new access token
	return this.http.post(this.API_ENDPOINT + '/token.php', body, options);
}

call(method, params) {
	// check if access_token is expired, if so use refresh token to get a new one
	let now = Date.now() / 1000;
	if (this.authData.access_expires < now) {
		return this.refreshToken().flatMap(res => {
			let res_json = res.json();
			this.setAuthData(res_json);
			return this.call(method, params);
		});
	} else {
		...
		// make the API call
		return this.http.post(this.API_ENDPOINT + '/resource.php', body, options)
			.map(res => res.json());
	}
}

It seems like the flatMap() vs map() was the key – here’s another couple references about the topic:


Thanks again!
Brian


Controlling Nav (accessing Pages) from an Injectable service