Authentication
The Meetup API uses OAuth 2.0 for authentication, which requires a user to go through an authentication flow where they have to accept that your app gets access to their Meetup data.
A successful authentication gives you both an access token and a refresh token. In theory access tokens are short-lived (an hour according to the Meetup authentication), so the refresh token can be used to obtain a new short-lived access token for the user. In practice, Meetup access token don't seem to expire.
From an access token
An access token is obtained either by sending the user through Meetup's OAuth authentication flow, or from exchanging a refresh token for an access token.
The MeetupHttpService
class is the main entry point for accessing the Meetup API, and you can create a new instance by using the MeetupHttpService.CreateFromAccessToken
method:
@using Microsoft.AspNetCore.Mvc.Razor
@using Skybrud.Social.Meetup
@inherits RazorPage
@{
MeetupHttpService meetup = MeetupHttpService.CreateFromAccessToken("Your access token here");
}
Notice that this step it self doesn't involve any requests to the Meetup API.
From a refresh token
You can also create a new MeetupHttpService
instance from the user's refresh token along with the client ID and client secret of your Meetup app. Under the hood, the package use these three to request the Meetup API for a new access token for the user.
@using Microsoft.AspNetCore.Mvc.Razor
@using Skybrud.Social.Meetup
@inherits RazorPage
@{
MeetupHttpService meetup = MeetupHttpService.CreateFromRefreshToken("Your client ID", "Your client secret", "Your refresh token");
}
As the CreateFromRefreshToken
method results in a request to the Meetup API, you should ideally cache the created MeetupHttpService
. The Meetup API indicates that access tokens live for an hour, but in practice Meetup access tokens don't seem to expire.
Caching the HTTP service
As mentioned before, when getting a new MeetupHttpService
instance from a refresh token, it's advised to cache the created instance, so we don't keep creating new access token when not necessary.
The example below uses Umbraco and dependency injection to create the MeetupApiAccessor
class. In Umbraco, the AppCaches
exposes different types of cache, and for our use case, we can use it's RuntimeCache
property to cache instances for the duration of the application - or for a specified duration in this case.
As the Meetup API documentation mentions that access tokens expire in an hour, we cache the created MeetupHttpService
for just short of an hour to account for different machines not having their time 100% in sync.
using System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Skybrud.Essentials.Http.Exceptions;
using Skybrud.Social.Meetup;
using Skybrud.Social.Meetup.OAuth;
using Skybrud.Social.Meetup.Responses.Authentication;
using Umbraco.Cms.Core.Cache;
namespace code.Features.Meetup {
public class MeetupApiAccessor {
private readonly ILogger<MeetupApiAccessor> _logger;
private readonly IConfiguration _configuration;
private readonly AppCaches _appCaches;
public MeetupApiAccessor(ILogger<MeetupApiAccessor> logger, IConfiguration configuration, AppCaches appCaches) {
_logger = logger;
_configuration = configuration;
_appCaches = appCaches;
}
/// <summary>
///
/// </summary>
public MeetupHttpService Api => (MeetupHttpService) _appCaches.RuntimeCache
.Get("MeetupApiAccessor", Factory, TimeSpan.FromMinutes(59))!;
private MeetupHttpService Factory() {
string clientId = _configuration.GetSection("Limbo:Integrations:Meetup:ClientId").Value;
string clientSecret = _configuration.GetSection("Limbo:Integrations:Meetup:ClientSecret").Value;
string refreshToken = _configuration.GetSection("Limbo:Integrations:Meetup:RefreshToken").Value;
if (string.IsNullOrWhiteSpace(clientId)) throw new Exception("Required 'Limbo:Integrations:Meetup:ClientId' not specified in app settings.");
if (string.IsNullOrWhiteSpace(clientSecret)) throw new Exception("Required 'Limbo:Integrations:Meetup:ClientSecret' not specified in app settings.");
if (string.IsNullOrWhiteSpace(refreshToken)) throw new Exception("Required 'Limbo:Integrations:Meetup:RefreshToken' not specified in app settings.");
MeetupOAuthClient client = new() {
ClientId = clientId,
ClientSecret = clientSecret
};
try {
MeetupTokenResponse response = client.GetAccessTokenFromRefreshToken(refreshToken);
client.AccessToken = response.Body.AccessToken;
return MeetupHttpService.CreateFromOAuthClient(client);
} catch (HttpException ex) {
_logger.LogError(ex, "Failed obtaining new Meetup access token.");
throw new Exception("Failed obtaining new Meetup access token.", ex);
}
}
}
}