1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.car.debuggingrestrictioncontroller.ui.token; 18 19 import android.util.Base64; 20 import androidx.annotation.NonNull; 21 import androidx.lifecycle.LiveData; 22 import androidx.lifecycle.MutableLiveData; 23 import androidx.lifecycle.ViewModel; 24 import com.android.car.debuggingrestrictioncontroller.BuildConfig; 25 import com.android.car.debuggingrestrictioncontroller.auth.TokenPayload; 26 import com.android.car.debuggingrestrictioncontroller.auth.TokenValidator; 27 import com.google.firebase.functions.FirebaseFunctions; 28 import java.security.GeneralSecurityException; 29 import java.security.SecureRandom; 30 import java.util.HashMap; 31 import java.util.Map; 32 33 public class TokenViewModel extends ViewModel { 34 35 private static final String TAG = TokenViewModel.class.getSimpleName(); 36 private static final String TOKEN_ISSUER_API_NAME = BuildConfig.TOKEN_ISSUER_API_NAME; 37 38 private static final String FIELD_NONCE = "nonce"; 39 private static final String FIELD_TOKEN = "token"; 40 41 private static final SecureRandom SECURE_RANDOM = new SecureRandom(); 42 private final FirebaseFunctions firebaseFunctions = FirebaseFunctions.getInstance(); 43 private final MutableLiveData<TokenResult> tokenResult = new MutableLiveData<>(); 44 getTokenResult()45 LiveData<TokenResult> getTokenResult() { 46 return tokenResult; 47 } 48 requestAccessToken(@onNull Map<String, Object> query)49 public void requestAccessToken(@NonNull Map<String, Object> query) { 50 byte[] nonceBytes = new byte[16]; 51 SECURE_RANDOM.nextBytes(nonceBytes); 52 final String nonce = Base64.encodeToString(nonceBytes, Base64.DEFAULT); 53 query.put(FIELD_NONCE, nonce); 54 55 firebaseFunctions 56 .getHttpsCallable(TOKEN_ISSUER_API_NAME) 57 .call(query) 58 .continueWith( 59 task -> { 60 @SuppressWarnings("unchecked") 61 Map<String, Object> result = (Map<String, Object>) task.getResult().getData(); 62 return (String) result.get(FIELD_TOKEN); 63 }) 64 .addOnCompleteListener( 65 task -> { 66 if (task.isSuccessful()) { 67 try { 68 TokenPayload validatedPayload = 69 TokenValidator.parseAndVerify(task.getResult(), nonce); 70 HashMap<String, Boolean> approvedRestrictions = 71 validatedPayload.getRestrictions(); 72 tokenResult.postValue(new TokenResult(new TokenView("OK", approvedRestrictions))); 73 } catch (GeneralSecurityException e) { 74 tokenResult.postValue(new TokenResult("Invalid access token")); 75 } 76 } else { 77 tokenResult.postValue(new TokenResult(task.getException().getMessage())); 78 } 79 }); 80 } 81 } 82