mirror of
https://github.com/bitwarden/mobile
synced 2025-12-16 00:03:22 +00:00
Converted auth to identity server endpoints and utilize bearer2 access token
This commit is contained in:
210
src/App/Services/TokenService.cs
Normal file
210
src/App/Services/TokenService.cs
Normal file
@@ -0,0 +1,210 @@
|
||||
using System;
|
||||
using Bit.App.Abstractions;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.App.Services
|
||||
{
|
||||
public class TokenService : ITokenService
|
||||
{
|
||||
private const string TokenKey = "accessToken";
|
||||
private const string RefreshTokenKey = "refreshToken";
|
||||
private const string AuthBearerKey = "token";
|
||||
|
||||
private readonly ISecureStorageService _secureStorage;
|
||||
|
||||
private string _token;
|
||||
private dynamic _decodedToken;
|
||||
private string _refreshToken;
|
||||
private string _authBearer;
|
||||
|
||||
private static readonly DateTime _epoc = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
public TokenService(ISecureStorageService secureStorage)
|
||||
{
|
||||
_secureStorage = secureStorage;
|
||||
}
|
||||
|
||||
public string Token
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_token != null)
|
||||
{
|
||||
return _token;
|
||||
}
|
||||
|
||||
var tokenBytes = _secureStorage.Retrieve(TokenKey);
|
||||
if(tokenBytes == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
_token = Encoding.UTF8.GetString(tokenBytes, 0, tokenBytes.Length);
|
||||
return _token;
|
||||
}
|
||||
set
|
||||
{
|
||||
if(value != null)
|
||||
{
|
||||
var tokenBytes = Encoding.UTF8.GetBytes(value);
|
||||
_secureStorage.Store(TokenKey, tokenBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
_secureStorage.Delete(TokenKey);
|
||||
RefreshToken = null;
|
||||
AuthBearer = null;
|
||||
}
|
||||
|
||||
_decodedToken = null;
|
||||
_token = value;
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime TokenExpiration
|
||||
{
|
||||
get
|
||||
{
|
||||
var decoded = DecodeToken();
|
||||
long exp = 0;
|
||||
if(decoded?.exp != null || !long.TryParse(decoded.exp, out exp))
|
||||
{
|
||||
throw new InvalidOperationException("No exp in token.");
|
||||
}
|
||||
|
||||
return _epoc.AddSeconds(Convert.ToDouble(exp));
|
||||
}
|
||||
}
|
||||
|
||||
public bool TokenExpired => DateTime.UtcNow < TokenExpiration;
|
||||
public TimeSpan TokenTimeRemaining => TokenExpiration - DateTime.UtcNow;
|
||||
public bool TokenNeedseRefresh => TokenTimeRemaining.TotalMinutes < 5;
|
||||
public string TokenUserId => DecodeToken()?.sub;
|
||||
public string TokenEmail => DecodeToken()?.email;
|
||||
public string TokenName => DecodeToken()?.name;
|
||||
|
||||
public string RefreshToken
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_refreshToken != null)
|
||||
{
|
||||
return _refreshToken;
|
||||
}
|
||||
|
||||
var tokenBytes = _secureStorage.Retrieve(RefreshTokenKey);
|
||||
if(tokenBytes == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
_refreshToken = Encoding.UTF8.GetString(tokenBytes, 0, tokenBytes.Length);
|
||||
return _refreshToken;
|
||||
}
|
||||
set
|
||||
{
|
||||
if(value != null)
|
||||
{
|
||||
var tokenBytes = Encoding.UTF8.GetBytes(value);
|
||||
_secureStorage.Store(RefreshTokenKey, tokenBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
_secureStorage.Delete(RefreshTokenKey);
|
||||
}
|
||||
|
||||
_refreshToken = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string AuthBearer
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_authBearer != null)
|
||||
{
|
||||
return _authBearer;
|
||||
}
|
||||
|
||||
var tokenBytes = _secureStorage.Retrieve(AuthBearerKey);
|
||||
if(tokenBytes == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
_authBearer = Encoding.UTF8.GetString(tokenBytes, 0, tokenBytes.Length);
|
||||
return _authBearer;
|
||||
}
|
||||
set
|
||||
{
|
||||
if(value != null)
|
||||
{
|
||||
var tokenBytes = Encoding.UTF8.GetBytes(value);
|
||||
_secureStorage.Store(AuthBearerKey, tokenBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
_secureStorage.Delete(AuthBearerKey);
|
||||
}
|
||||
|
||||
_authBearer = value;
|
||||
}
|
||||
}
|
||||
|
||||
public dynamic DecodeToken()
|
||||
{
|
||||
if(_decodedToken != null)
|
||||
{
|
||||
return _decodedToken;
|
||||
}
|
||||
|
||||
if(Token == null)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(Token)} not found.");
|
||||
}
|
||||
|
||||
var parts = Token.Split('.');
|
||||
if(parts.Length != 3)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(Token)} must have 3 parts");
|
||||
}
|
||||
|
||||
var decodedBytes = Base64UrlDecode(parts[1]);
|
||||
if(decodedBytes == null || decodedBytes.Length < 1)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(Token)} must have 3 parts");
|
||||
}
|
||||
|
||||
_decodedToken = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(decodedBytes, 0, decodedBytes.Length));
|
||||
return _decodedToken;
|
||||
}
|
||||
|
||||
private static byte[] Base64UrlDecode(string input)
|
||||
{
|
||||
var output = input;
|
||||
// 62nd char of encoding
|
||||
output = output.Replace('-', '+');
|
||||
// 63rd char of encoding
|
||||
output = output.Replace('_', '/');
|
||||
// Pad with trailing '='s
|
||||
switch(output.Length % 4)
|
||||
{
|
||||
case 0:
|
||||
// No pad chars in this case
|
||||
break;
|
||||
case 2:
|
||||
// Two pad chars
|
||||
output += "=="; break;
|
||||
case 3:
|
||||
// One pad char
|
||||
output += "="; break;
|
||||
default:
|
||||
throw new InvalidOperationException("Illegal base64url string!");
|
||||
}
|
||||
|
||||
// Standard base64 decoder
|
||||
return Convert.FromBase64String(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user